diff --git a/src/coreclr/jit/compiler.h b/src/coreclr/jit/compiler.h index c8d7fe49b62a01..a8696581b02367 100644 --- a/src/coreclr/jit/compiler.h +++ b/src/coreclr/jit/compiler.h @@ -7057,6 +7057,7 @@ class Compiler unsigned optCSEstart; // The first local variable number that is a CSE unsigned optCSEattempt; // The number of CSEs attempted so far. unsigned optCSEcount; // The total count of CSEs introduced. + unsigned optCSEunmarks; // Number of CSE trees unmarked weight_t optCSEweight; // The weight of the current block when we are doing PerformCSE CSE_HeuristicCommon* optCSEheuristic; // CSE Heuristic to use for this method diff --git a/src/coreclr/jit/jitconfigvalues.h b/src/coreclr/jit/jitconfigvalues.h index d84c2f79b7dda0..5d074135ba013a 100644 --- a/src/coreclr/jit/jitconfigvalues.h +++ b/src/coreclr/jit/jitconfigvalues.h @@ -379,7 +379,7 @@ CONFIG_INTEGER(JitConstCSE, W("JitConstCSE"), 0) // If nonzero, use the greedy RL policy. // -CONFIG_INTEGER(JitRLCSEGreedy, W("JitRLCSEGreedy"), 0) +CONFIG_INTEGER(JitRLCSEGreedy, W("JitRLCSEGreedy"), 1) // If nonzero, dump out details of parameterized policy evaluation and // gradient updates diff --git a/src/coreclr/jit/optcse.cpp b/src/coreclr/jit/optcse.cpp index 0166d4cd47431b..cd554f02e56372 100644 --- a/src/coreclr/jit/optcse.cpp +++ b/src/coreclr/jit/optcse.cpp @@ -164,6 +164,11 @@ bool Compiler::optUnmarkCSE(GenTree* tree) // 2. Unmark the CSE information in the node tree->gtCSEnum = NO_CSE; + + // 3. Leave breadcrumbs so we know some dsc was altered + + optCSEunmarks++; + return true; } else @@ -2436,10 +2441,12 @@ void CSE_HeuristicParameterized::GreedyPolicy() // const int numCandidates = m_pCompiler->optCSECandidateCount; ArrayStack choices(m_pCompiler->getAllocator(CMK_CSE), numCandidates + 1); + unsigned numUnmarked = m_pCompiler->optCSEunmarks; + bool recomputeFeatures = true; while (true) { - Choice& choice = ChooseGreedy(choices); + Choice& choice = ChooseGreedy(choices, recomputeFeatures); CSEdsc* const dsc = choice.m_dsc; #ifdef DEBUG @@ -2472,7 +2479,16 @@ void CSE_HeuristicParameterized::GreedyPolicy() JITDUMP("\n"); PerformCSE(&candidate); - madeChanges = true; + madeChanges = true; + choice.m_performed = true; + + // If performing this CSE impacted other CSEs, we need to + // recompute all cse features. + // + unsigned newNumUnmarked = m_pCompiler->optCSEunmarks; + assert(newNumUnmarked >= numUnmarked); + recomputeFeatures = (numUnmarked != newNumUnmarked); + numUnmarked = newNumUnmarked; } return; @@ -2575,7 +2591,7 @@ void CSE_HeuristicParameterized::GetFeatures(CSEdsc* cse, double* features) unsigned maxPostorderNum = 0; BasicBlock* minPostorderBlock = nullptr; BasicBlock* maxPostorderBlock = nullptr; - for (treeStmtLst* treeList = cse->csdTreeList; treeList != nullptr && !isMakeCse; treeList = treeList->tslNext) + for (treeStmtLst* treeList = cse->csdTreeList; treeList != nullptr; treeList = treeList->tslNext) { BasicBlock* const treeBlock = treeList->tslBlock; unsigned postorderNum = treeBlock->bbPostorderNum; @@ -2616,7 +2632,6 @@ void CSE_HeuristicParameterized::GetFeatures(CSEdsc* cse, double* features) // LSRA "is live across call" // bool isLiveAcrossCallLSRA = isLiveAcrossCall; - if (!isLiveAcrossCallLSRA) { unsigned count = 0; @@ -2630,7 +2645,6 @@ void CSE_HeuristicParameterized::GetFeatures(CSEdsc* cse, double* features) } } } - features[23] = booleanScale * isLiveAcrossCallLSRA; } @@ -2748,6 +2762,10 @@ double CSE_HeuristicParameterized::StoppingPreference() // ChooseGreedy: examine candidates and choose the next CSE to perform // via greedy policy // +// Arguments: +// choices -- array of choices, possibly already filled in +// recompute -- if true, rebuild the choice array from scratch +// // Returns: // Choice of CSE to perform // @@ -2755,10 +2773,25 @@ double CSE_HeuristicParameterized::StoppingPreference() // Picks the most-preferred candidate. // If there is a tie, picks stop, or the lowest cse index. // -CSE_HeuristicParameterized::Choice& CSE_HeuristicParameterized::ChooseGreedy(ArrayStack& choices) +CSE_HeuristicParameterized::Choice& CSE_HeuristicParameterized::ChooseGreedy(ArrayStack& choices, + bool recompute) { - choices.Reset(); - BuildChoices(choices); + if (recompute) + { + choices.Reset(); + BuildChoices(choices); + } + else + { + // Always recompute the stopping preference as this + // reflects ambient state after each CSE. + // + // By convention, this is at TopRef(0). + // + Choice& stopping = choices.TopRef(0); + assert(stopping.m_dsc == nullptr); + stopping.m_preference = StoppingPreference(); + } // Find the maximally preferred case. // @@ -2766,8 +2799,14 @@ CSE_HeuristicParameterized::Choice& CSE_HeuristicParameterized::ChooseGreedy(Arr for (int i = 1; i < choices.Height(); i++) { - Choice& choice = choices.TopRef(i); - Choice& bestChoice = choices.TopRef(choiceNum); + const Choice& choice = choices.TopRef(i); + + if (choice.m_performed == true) + { + continue; + } + + const Choice& bestChoice = choices.TopRef(choiceNum); const double delta = choice.m_preference - bestChoice.m_preference; @@ -2811,6 +2850,8 @@ CSE_HeuristicParameterized::Choice& CSE_HeuristicParameterized::ChooseGreedy(Arr // void CSE_HeuristicParameterized::BuildChoices(ArrayStack& choices) { + JITDUMP("Building choice array...\n"); + for (unsigned i = 0; i < m_pCompiler->optCSECandidateCount; i++) { CSEdsc* const dsc = sortTab[i]; @@ -2893,9 +2934,15 @@ void CSE_HeuristicParameterized::DumpChoices(ArrayStack& choices, int hi { for (int i = 0; i < choices.Height(); i++) { - Choice& choice = choices.TopRef(i); - CSEdsc* const cse = choice.m_dsc; - const char* msg = i == highlight ? "=>" : " "; + const Choice& choice = choices.TopRef(i); + + if (choice.m_performed == true) + { + continue; + } + + CSEdsc* const cse = choice.m_dsc; + const char* msg = (i == highlight) ? "=>" : " "; if (cse != nullptr) { printf("%s%2d: " FMT_CSE " preference %10.7f likelihood %10.7f\n", msg, i, cse->csdIndex, @@ -2920,9 +2967,15 @@ void CSE_HeuristicParameterized::DumpChoices(ArrayStack& choices, CSEdsc { for (int i = 0; i < choices.Height(); i++) { - Choice& choice = choices.TopRef(i); - CSEdsc* const cse = choice.m_dsc; - const char* msg = cse == highlight ? "=>" : " "; + const Choice& choice = choices.TopRef(i); + + if (choice.m_performed == true) + { + continue; + } + + CSEdsc* const cse = choice.m_dsc; + const char* msg = (cse == highlight) ? "=>" : " "; if (cse != nullptr) { printf("%s%2d: " FMT_CSE " preference %10.7f likelihood %10.7f\n", msg, i, cse->csdIndex, @@ -4422,50 +4475,62 @@ bool CSE_HeuristicCommon::IsCompatibleType(var_types cseLclVarTyp, var_types exp return false; } -// PerformCSE() takes a successful candidate and performs the appropriate replacements: +//------------------------------------------------------------------------ +// PerformCSE: takes a successful candidate and performs the appropriate replacements +// +// Arguments: +// successfulCandidate - cse candidate to perform // // It will replace all of the CSE defs with assignments to a new "cse0" LclVar // and will replace all of the CSE uses with reads of the "cse0" LclVar // // It will also put cse0 into SSA if there is just one def. +// void CSE_HeuristicCommon::PerformCSE(CSE_Candidate* successfulCandidate) { AdjustHeuristic(successfulCandidate); + CSEdsc* const dsc = successfulCandidate->CseDsc(); #ifdef DEBUG // Setup the message arg for lvaGrabTemp() // - const char* grabTempMessage = "CSE - unknown"; + const char* heuristicTempMessage = ""; if (successfulCandidate->IsAggressive()) { - grabTempMessage = "CSE - aggressive"; + heuristicTempMessage = ": aggressive"; } else if (successfulCandidate->IsModerate()) { - grabTempMessage = "CSE - moderate"; + heuristicTempMessage = ": moderate"; } else if (successfulCandidate->IsConservative()) { - grabTempMessage = "CSE - conservative"; + heuristicTempMessage = ": conservative"; } else if (successfulCandidate->IsStressCSE()) { - grabTempMessage = "CSE - stress mode"; + heuristicTempMessage = ": stress"; } else if (successfulCandidate->IsRandom()) { - grabTempMessage = "CSE - random"; + heuristicTempMessage = ": random"; } -#endif // DEBUG - /* Introduce a new temp for the CSE */ + const char* const grabTempMessage = m_pCompiler->printfAlloc(FMT_CSE "%s", dsc->csdIndex, heuristicTempMessage); + + // Add this candidate to the CSE sequence + // + m_sequence->push_back(dsc->csdIndex); + +#endif // DEBUG - // we will create a long lifetime temp for the new CSE LclVar + // Allocate a CSE temp + // unsigned cseLclVarNum = m_pCompiler->lvaGrabTemp(false DEBUGARG(grabTempMessage)); var_types cseLclVarTyp = genActualType(successfulCandidate->Expr()->TypeGet()); - LclVarDsc* lclDsc = m_pCompiler->lvaGetDesc(cseLclVarNum); + LclVarDsc* const lclDsc = m_pCompiler->lvaGetDesc(cseLclVarNum); if (cseLclVarTyp == TYP_STRUCT) { m_pCompiler->lvaSetStruct(cseLclVarNum, successfulCandidate->Expr()->GetLayout(m_pCompiler), false); @@ -4474,6 +4539,7 @@ void CSE_HeuristicCommon::PerformCSE(CSE_Candidate* successfulCandidate) lclDsc->lvIsCSE = true; // Record that we created a new LclVar for use as a CSE temp + // m_addCSEcount++; m_pCompiler->optCSEcount++; m_pCompiler->Metrics.CseCount++; @@ -4484,11 +4550,9 @@ void CSE_HeuristicCommon::PerformCSE(CSE_Candidate* successfulCandidate) // // Later we will unmark any nested CSE's for the CSE uses. // - CSEdsc* dsc = successfulCandidate->CseDsc(); - INDEBUG(m_sequence->push_back(dsc->csdIndex)); - // If there's just a single def for the CSE, we'll put this // CSE into SSA form on the fly. We won't need any PHIs. + // unsigned cseSsaNum = SsaConfig::RESERVED_SSA_NUM; LclSsaVarDsc* ssaVarDsc = nullptr; diff --git a/src/coreclr/jit/optcse.h b/src/coreclr/jit/optcse.h index 3d1c7f0702ba96..550f754f6a8b6e 100644 --- a/src/coreclr/jit/optcse.h +++ b/src/coreclr/jit/optcse.h @@ -151,12 +151,14 @@ class CSE_HeuristicParameterized : public CSE_HeuristicCommon protected: struct Choice { - Choice(CSEdsc* dsc, double preference) : m_dsc(dsc), m_preference(preference), m_softmax(0) + Choice(CSEdsc* dsc, double preference) : m_dsc(dsc), m_preference(preference), m_softmax(0), m_performed(false) { } + CSEdsc* m_dsc; double m_preference; double m_softmax; + bool m_performed; }; enum @@ -185,7 +187,7 @@ class CSE_HeuristicParameterized : public CSE_HeuristicCommon double StoppingPreference(); void BuildChoices(ArrayStack& choices); - Choice& ChooseGreedy(ArrayStack& choices); + Choice& ChooseGreedy(ArrayStack& choices, bool recompute); virtual const char* Name() const { diff --git a/src/coreclr/jit/optimizer.cpp b/src/coreclr/jit/optimizer.cpp index 169d5f26774931..d9e9ee4969ed1e 100644 --- a/src/coreclr/jit/optimizer.cpp +++ b/src/coreclr/jit/optimizer.cpp @@ -34,6 +34,7 @@ void Compiler::optInit() optCSECandidateCount = 0; optCSEattempt = 0; optCSEheuristic = nullptr; + optCSEunmarks = 0; } DataFlow::DataFlow(Compiler* pCompiler) : m_pCompiler(pCompiler)