diff options
-rw-r--r-- | src/jit/assertionprop.cpp | 4 | ||||
-rw-r--r-- | src/jit/compiler.h | 12 | ||||
-rw-r--r-- | src/jit/compiler.hpp | 44 | ||||
-rw-r--r-- | src/jit/copyprop.cpp | 8 | ||||
-rw-r--r-- | src/jit/earlyprop.cpp | 4 | ||||
-rw-r--r-- | src/jit/morph.cpp | 2 | ||||
-rw-r--r-- | src/jit/optimizer.cpp | 6 | ||||
-rw-r--r-- | src/jit/ssabuilder.cpp | 79 | ||||
-rw-r--r-- | src/jit/ssabuilder.h | 2 | ||||
-rw-r--r-- | src/jit/valuenum.cpp | 40 |
10 files changed, 115 insertions, 86 deletions
diff --git a/src/jit/assertionprop.cpp b/src/jit/assertionprop.cpp index ebf118038b..87cf677b5e 100644 --- a/src/jit/assertionprop.cpp +++ b/src/jit/assertionprop.cpp @@ -1296,8 +1296,8 @@ AssertionIndex Compiler::optCreateAssertion(GenTree* op1, 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; } diff --git a/src/jit/compiler.h b/src/jit/compiler.h index d3179509d5..a7629fcbe7 100644 --- a/src/jit/compiler.h +++ b/src/jit/compiler.h @@ -330,6 +330,8 @@ public: 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; @@ -3025,6 +3027,13 @@ public: 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 @@ -4133,9 +4142,6 @@ public: 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); diff --git a/src/jit/compiler.hpp b/src/jit/compiler.hpp index 45d0828582..cdaa04a53d 100644 --- a/src/jit/compiler.hpp +++ b/src/jit/compiler.hpp @@ -4276,48 +4276,6 @@ bool Compiler::fgStructTempNeedsExplicitZeroInit(LclVarDsc* varDsc, BasicBlock* } /*****************************************************************************/ -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) { if (op->gtFlags & GTF_VAR_USEASG) @@ -4336,7 +4294,7 @@ 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; } diff --git a/src/jit/copyprop.cpp b/src/jit/copyprop.cpp index a83682df97..a1beccacb5 100644 --- a/src/jit/copyprop.cpp +++ b/src/jit/copyprop.cpp @@ -38,7 +38,7 @@ void Compiler::optBlockCopyPropPopStacks(BasicBlock* block, LclNumToGenTreePtrSt continue; } unsigned lclNum = tree->gtLclVarCommon.gtLclNum; - if (fgExcludeFromSsa(lclNum)) + if (!lvaInSsa(lclNum)) { continue; } @@ -153,8 +153,8 @@ void Compiler::optCopyProp(BasicBlock* block, GenTree* stmt, GenTree* tree, LclN } unsigned lclNum = tree->AsLclVarCommon()->GetLclNum(); - // Skip address exposed variables. - if (fgExcludeFromSsa(lclNum)) + // Skip non-SSA variables. + if (!lvaInSsa(lclNum)) { return; } @@ -287,7 +287,7 @@ void Compiler::optCopyProp(BasicBlock* block, GenTree* stmt, GenTree* tree, LclN */ bool Compiler::optIsSsaLocal(GenTree* tree) { - return tree->IsLocal() && !fgExcludeFromSsa(tree->AsLclVarCommon()->GetLclNum()); + return tree->IsLocal() && lvaInSsa(tree->AsLclVarCommon()->GetLclNum()); } //------------------------------------------------------------------------------ diff --git a/src/jit/earlyprop.cpp b/src/jit/earlyprop.cpp index 787dc84de5..edbcefdf69 100644 --- a/src/jit/earlyprop.cpp +++ b/src/jit/earlyprop.cpp @@ -273,7 +273,7 @@ GenTree* Compiler::optEarlyPropRewriteTree(GenTree* tree) return nullptr; } - if (!objectRefPtr->OperIsScalarLocal() || fgExcludeFromSsa(objectRefPtr->AsLclVarCommon()->GetLclNum())) + if (!objectRefPtr->OperIsScalarLocal() || !lvaInSsa(objectRefPtr->AsLclVarCommon()->GetLclNum())) { return nullptr; @@ -442,7 +442,7 @@ GenTree* Compiler::optPropGetValueRec(unsigned lclNum, unsigned ssaNum, optPropK 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(); diff --git a/src/jit/morph.cpp b/src/jit/morph.cpp index 806a3cfba7..dd570d3256 100644 --- a/src/jit/morph.cpp +++ b/src/jit/morph.cpp @@ -17712,7 +17712,7 @@ void Compiler::fgRetypeImplicitByRefArgs() // 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. diff --git a/src/jit/optimizer.cpp b/src/jit/optimizer.cpp index 83f3a021e2..73c79c9c2a 100644 --- a/src/jit/optimizer.cpp +++ b/src/jit/optimizer.cpp @@ -7178,7 +7178,7 @@ bool Compiler::optTreeIsValidAtLoopHead(GenTree* tree, unsigned lnum) unsigned lclNum = lclVar->gtLclNum; // The lvlVar must be have an Ssa tracked lifetime - if (fgExcludeFromSsa(lclNum)) + if (!lvaInSsa(lclNum)) { return false; } @@ -7627,7 +7627,7 @@ void Compiler::optComputeLoopSideEffectsOfBlock(BasicBlock* blk) { // 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(); @@ -7717,7 +7717,7 @@ void Compiler::optComputeLoopSideEffectsOfBlock(BasicBlock* blk) if (rhsVN != ValueNumStore::NoVN) { rhsVN = vnStore->VNNormVal(rhsVN); - if (!fgExcludeFromSsa(lhsLcl->GetLclNum())) + if (lvaInSsa(lhsLcl->GetLclNum())) { lvaTable[lhsLcl->GetLclNum()] .GetPerSsaData(lhsLcl->GetSsaNum()) diff --git a/src/jit/ssabuilder.cpp b/src/jit/ssabuilder.cpp index b4bb8afa67..d7347c2d87 100644 --- a/src/jit/ssabuilder.cpp +++ b/src/jit/ssabuilder.cpp @@ -687,10 +687,6 @@ void SsaBuilder::InsertPhiFunctions(BasicBlock** postOrder, int count) { 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); @@ -722,7 +718,7 @@ void SsaBuilder::InsertPhiFunctions(BasicBlock** postOrder, int count) 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; @@ -914,7 +910,7 @@ void SsaBuilder::TreeRenameVariables(GenTree* tree, BasicBlock* block, SsaRename 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; @@ -1526,20 +1522,25 @@ void SsaBuilder::RenameVariables(BlkToBlkVectorMap* domTree, SsaRenameState* pRe // 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); } } @@ -1729,6 +1730,16 @@ void SsaBuilder::Build() 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); @@ -1791,6 +1802,52 @@ void SsaBuilder::SetupBBRoot() } } +//------------------------------------------------------------------------ +// 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() diff --git a/src/jit/ssabuilder.h b/src/jit/ssabuilder.h index 27934c8aac..eab25549df 100644 --- a/src/jit/ssabuilder.h +++ b/src/jit/ssabuilder.h @@ -24,6 +24,8 @@ private: m_pCompiler->EndPhase(phase); } + bool IncludeInSsa(unsigned lclNum); + public: // Constructor SsaBuilder(Compiler* pCompiler); diff --git a/src/jit/valuenum.cpp b/src/jit/valuenum.cpp index 8d07436402..9ab6098b33 100644 --- a/src/jit/valuenum.cpp +++ b/src/jit/valuenum.cpp @@ -4756,9 +4756,16 @@ void Compiler::fgValueNumber() // 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 @@ -4767,13 +4774,13 @@ void Compiler::fgValueNumber() // 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) @@ -4804,7 +4811,7 @@ void Compiler::fgValueNumber() 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; @@ -4816,12 +4823,12 @@ void Compiler::fgValueNumber() } 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 @@ -5530,7 +5537,7 @@ void Compiler::fgValueNumberBlockAssignment(GenTree* tree, bool evalAsgLhsInd) // 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)); @@ -5598,7 +5605,7 @@ void Compiler::fgValueNumberBlockAssignment(GenTree* tree, bool evalAsgLhsInd) 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)); @@ -5652,7 +5659,7 @@ void Compiler::fgValueNumberBlockAssignment(GenTree* tree, bool evalAsgLhsInd) { 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; @@ -5681,7 +5688,7 @@ void Compiler::fgValueNumberBlockAssignment(GenTree* tree, bool evalAsgLhsInd) { unsigned rhsLclNum = rhsLclVarTree->GetLclNum(); rhsVarDsc = &lvaTable[rhsLclNum]; - if (fgExcludeFromSsa(rhsLclNum) || rhsFldSeq == FieldSeqStore::NotAField()) + if (!lvaInSsa(rhsLclNum) || rhsFldSeq == FieldSeqStore::NotAField()) { isNewUniq = true; } @@ -5983,7 +5990,7 @@ void Compiler::fgValueNumberTree(GenTree* tree, bool evalAsgLhsInd) 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). @@ -5994,7 +6001,7 @@ void Compiler::fgValueNumberTree(GenTree* tree, bool evalAsgLhsInd) 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. @@ -6363,7 +6370,7 @@ void Compiler::fgValueNumberTree(GenTree* tree, bool evalAsgLhsInd) wasLocal = true; - if (!fgExcludeFromSsa(lclNum)) + if (lvaInSsa(lclNum)) { FieldSeqNode* fieldSeq = vnStore->FieldSeqVNToFieldSeq(funcApp.m_args[1]); @@ -6716,7 +6723,7 @@ void Compiler::fgValueNumberTree(GenTree* tree, bool evalAsgLhsInd) { FieldSeqNode* fieldSeq = nullptr; ValueNum newVN = ValueNumStore::NoVN; - if (fgExcludeFromSsa(arg->gtLclVarCommon.GetLclNum())) + if (!lvaInSsa(arg->gtLclVarCommon.GetLclNum())) { newVN = vnStore->VNForExpr(compCurBB, TYP_BYREF); } @@ -6928,8 +6935,7 @@ void Compiler::fgValueNumberTree(GenTree* tree, bool evalAsgLhsInd) 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(); |