Add code to switch to bitvec datastructure in cse optimization.
authorRussell C. Hadley <rhadley@microsoft.com>
Fri, 5 Aug 2016 23:07:21 +0000 (16:07 -0700)
committerrhadley <rhadley@microsoft.com>
Fri, 14 Oct 2016 22:21:48 +0000 (15:21 -0700)
This change will allow us to track more than 64 cse candidates.
Initally the data structure is set to the same limits (64) as is
currently used.  This gives us a no diffs.  Subsequent changes
will change these limits based on opportunities found in the benchmarks.

Commit migrated from https://github.com/dotnet/coreclr/commit/213468245fd42ec6829635c6f300f1aef972e26a

src/coreclr/src/jit/block.h
src/coreclr/src/jit/compiler.h
src/coreclr/src/jit/compiler.hpp
src/coreclr/src/jit/optcse.cpp
src/coreclr/src/jit/utils.cpp

index 4124f59..280b602 100644 (file)
@@ -30,17 +30,13 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
 #include "simplerhash.h"
 
 /*****************************************************************************/
-
+typedef BitVec EXPSET_TP;
 #if LARGE_EXPSET
-typedef unsigned __int64 EXPSET_TP;
 #define EXPSET_SZ 64
 #else
-typedef unsigned int EXPSET_TP;
 #define EXPSET_SZ 32
 #endif
 
-#define EXPSET_ALL ((EXPSET_TP)0 - 1)
-
 typedef BitVec          ASSERT_TP;
 typedef BitVec_ValArg_T ASSERT_VALARG_TP;
 typedef BitVec_ValRet_T ASSERT_VALRET_TP;
index b5da927..73df15e 100644 (file)
@@ -5238,6 +5238,11 @@ protected:
 
     static const int MIN_CSE_COST = 2;
 
+    // Keeps tracked cse indices
+    BitVecTraits* cseTraits;
+    EXPSET_TP     cseFull;
+    EXPSET_TP     cseEmpty;
+
     /* Generic list of nodes - used by the CSE logic */
 
     struct treeLst
index c53bf4c..0a0687a 100644 (file)
@@ -768,7 +768,8 @@ inline double getR8LittleEndian(const BYTE* ptr)
 
 /*****************************************************************************
  *
- *  Return the bitmask to use in the EXPSET_TP for the CSE with the given CSE index.
+ *  Return the normalized index to use in the EXPSET_TP for the CSE with
+ *  the given CSE index.
  *  Each GenTree has the following field:
  *    signed char       gtCSEnum;        // 0 or the CSE index (negated if def)
  *  So zero is reserved to mean this node is not a CSE
@@ -777,15 +778,15 @@ inline double getR8LittleEndian(const BYTE* ptr)
  *  This precondition is checked by the assert on the first line of this method.
  */
 
-inline EXPSET_TP genCSEnum2bit(unsigned index)
+inline unsigned int genCSEnum2bit(unsigned index)
 {
     assert((index > 0) && (index <= EXPSET_SZ));
 
-    return ((EXPSET_TP)1 << (index - 1));
+    return (index - 1);
 }
 
 #ifdef DEBUG
-const char* genES2str(EXPSET_TP set);
+const char* genES2str(BitVecTraits* traits, EXPSET_TP set);
 const char* refCntWtd2str(unsigned refCntWtd);
 #endif
 
index d23b4cd..f2d094f 100644 (file)
@@ -301,15 +301,15 @@ Compiler::fgWalkResult Compiler::optCSE_MaskHelper(GenTreePtr* pTree, fgWalkData
 
     if (IS_CSE_INDEX(tree->gtCSEnum))
     {
-        unsigned  cseIndex = GET_CSE_INDEX(tree->gtCSEnum);
-        EXPSET_TP cseBit   = genCSEnum2bit(cseIndex);
+        unsigned cseIndex = GET_CSE_INDEX(tree->gtCSEnum);
+        unsigned cseBit   = genCSEnum2bit(cseIndex);
         if (IS_CSE_DEF(tree->gtCSEnum))
         {
-            pUserData->CSE_defMask |= cseBit;
+            BitVecOps::AddElemD(comp->cseTraits, pUserData->CSE_defMask, cseBit);
         }
         else
         {
-            pUserData->CSE_useMask |= cseBit;
+            BitVecOps::AddElemD(comp->cseTraits, pUserData->CSE_useMask, cseBit);
         }
     }
 
@@ -321,8 +321,8 @@ Compiler::fgWalkResult Compiler::optCSE_MaskHelper(GenTreePtr* pTree, fgWalkData
 //
 void Compiler::optCSE_GetMaskData(GenTreePtr tree, optCSE_MaskData* pMaskData)
 {
-    pMaskData->CSE_defMask = 0;
-    pMaskData->CSE_useMask = 0;
+    pMaskData->CSE_defMask = BitVecOps::MakeCopy(cseTraits, cseEmpty);
+    pMaskData->CSE_useMask = BitVecOps::MakeCopy(cseTraits, cseEmpty);
     fgWalkTreePre(&tree, optCSE_MaskHelper, (void*)pMaskData);
 }
 
@@ -355,14 +355,14 @@ bool Compiler::optCSE_canSwap(GenTree* op1, GenTree* op2)
     optCSE_GetMaskData(op2, &op2MaskData);
 
     // We cannot swap if op1 contains a CSE def that is used by op2
-    if ((op1MaskData.CSE_defMask & op2MaskData.CSE_useMask) != 0)
+    if (!BitVecOps::IsEmptyIntersection(cseTraits, op1MaskData.CSE_defMask, op2MaskData.CSE_useMask))
     {
         canSwap = false;
     }
     else
     {
         // We also cannot swap if op2 contains a CSE def that is used by op1.
-        if ((op2MaskData.CSE_defMask & op1MaskData.CSE_useMask) != 0)
+        if (!BitVecOps::IsEmptyIntersection(cseTraits, op2MaskData.CSE_defMask, op1MaskData.CSE_useMask))
         {
             canSwap = false;
         }
@@ -495,6 +495,14 @@ void Compiler::optValnumCSE_Init()
     optCSEtab = nullptr;
 #endif
 
+    // Init traits and full/empty bitvectors.  This will be used to track the
+    // individual cse indexes.
+    cseTraits = new (getAllocator()) BitVecTraits(EXPSET_SZ, this);
+    cseFull   = BitVecOps::UninitVal();
+    cseEmpty  = BitVecOps::UninitVal();
+    BitVecOps::AssignNoCopy(cseTraits, cseFull, BitVecOps::MakeFull(cseTraits));
+    BitVecOps::AssignNoCopy(cseTraits, cseEmpty, BitVecOps::MakeEmpty(cseTraits));
+
     /* Allocate and clear the hash bucket table */
 
     optCSEhash = new (this, CMK_CSE) CSEdsc*[s_optCSEhashSize]();
@@ -631,8 +639,8 @@ unsigned Compiler::optValnumCSE_Index(GenTreePtr tree, GenTreePtr stmt)
 
         C_ASSERT((signed char)MAX_CSE_CNT == MAX_CSE_CNT);
 
-        unsigned  CSEindex = ++optCSECandidateCount;
-        EXPSET_TP CSEmask  = genCSEnum2bit(CSEindex);
+        unsigned CSEindex = ++optCSECandidateCount;
+        // EXPSET_TP  CSEmask  = genCSEnum2bit(CSEindex);
 
         /* Record the new CSE index in the hashDsc */
         hashDsc->csdIndex = CSEindex;
@@ -649,10 +657,11 @@ unsigned Compiler::optValnumCSE_Index(GenTreePtr tree, GenTreePtr stmt)
 #ifdef DEBUG
         if (verbose)
         {
+            EXPSET_TP tempMask = BitVecOps::MakeSingleton(cseTraits, genCSEnum2bit(CSEindex));
             printf("\nCSE candidate #%02u, vn=", CSEindex);
             vnPrint(vnlib, 0);
-            printf(" cseMask=%s in BB%02u, [cost=%2u, size=%2u]: \n", genES2str(genCSEnum2bit(CSEindex)),
-                   compCurBB->bbNum, tree->gtCostEx, tree->gtCostSz);
+            printf(" cseMask=%s in BB%02u, [cost=%2u, size=%2u]: \n", genES2str(cseTraits, tempMask), compCurBB->bbNum,
+                   tree->gtCostEx, tree->gtCostSz);
             gtDispTree(tree);
         }
 #endif // DEBUG
@@ -773,19 +782,18 @@ void Compiler::optValnumCSE_InitDataFlow()
         if (init_to_zero)
         {
             /* Initialize to {ZERO} prior to dataflow */
-
-            block->bbCseIn = 0;
+            block->bbCseIn = BitVecOps::MakeCopy(cseTraits, cseEmpty);
         }
         else
         {
             /* Initialize to {ALL} prior to dataflow */
-
-            block->bbCseIn = EXPSET_ALL;
+            block->bbCseIn = BitVecOps::MakeCopy(cseTraits, cseFull);
         }
-        block->bbCseOut = EXPSET_ALL;
+
+        block->bbCseOut = BitVecOps::MakeCopy(cseTraits, cseFull);
 
         /* Initialize to {ZERO} prior to locating the CSE candidates */
-        block->bbCseGen = 0;
+        block->bbCseGen = BitVecOps::MakeCopy(cseTraits, cseEmpty);
     }
 
     // We walk the set of CSE candidates and set the bit corresponsing to the CSEindex
@@ -801,7 +809,7 @@ void Compiler::optValnumCSE_InitDataFlow()
         while (lst != nullptr)
         {
             BasicBlock* block = lst->tslBlock;
-            block->bbCseGen |= genCSEnum2bit(CSEindex);
+            BitVecOps::AddElemD(cseTraits, block->bbCseGen, genCSEnum2bit(CSEindex));
             lst = lst->tslNext;
         }
     }
@@ -814,7 +822,7 @@ void Compiler::optValnumCSE_InitDataFlow()
         bool headerPrinted = false;
         for (BasicBlock* block = fgFirstBB; block; block = block->bbNext)
         {
-            if (block->bbCseGen != 0)
+            if (block->bbCseGen != nullptr)
             {
                 if (!headerPrinted)
                 {
@@ -822,7 +830,7 @@ void Compiler::optValnumCSE_InitDataFlow()
                     headerPrinted = true;
                 }
                 printf("BB%02u", block->bbNum);
-                printf(" cseGen = %s\n", genES2str(block->bbCseGen));
+                printf(" cseGen = %s\n", genES2str(cseTraits, block->bbCseGen));
             }
         }
     }
@@ -857,21 +865,24 @@ public:
     // At the start of the merge function of the dataflow equations, initialize premerge state (to detect changes.)
     void StartMerge(BasicBlock* block)
     {
-        m_preMergeOut = block->bbCseOut;
+        m_preMergeOut = BitVecOps::MakeCopy(m_pCompiler->cseTraits, block->bbCseOut);
     }
 
     // During merge, perform the actual merging of the predecessor's (since this is a forward analysis) dataflow flags.
     void Merge(BasicBlock* block, BasicBlock* predBlock, flowList* preds)
     {
-        block->bbCseIn &= predBlock->bbCseOut;
+        BitVecOps::IntersectionD(m_pCompiler->cseTraits, block->bbCseIn, predBlock->bbCseOut);
     }
 
     // At the end of the merge store results of the dataflow equations, in a postmerge state.
     bool EndMerge(BasicBlock* block)
     {
-        EXPSET_TP mergeOut = block->bbCseOut & (block->bbCseIn | block->bbCseGen);
-        block->bbCseOut    = mergeOut;
-        return (mergeOut != m_preMergeOut);
+        BitVecTraits* traits   = m_pCompiler->cseTraits;
+        EXPSET_TP     mergeOut = BitVecOps::MakeCopy(traits, block->bbCseIn);
+        BitVecOps::UnionD(traits, mergeOut, block->bbCseGen);
+        BitVecOps::IntersectionD(traits, mergeOut, block->bbCseOut);
+        BitVecOps::Assign(traits, block->bbCseOut, mergeOut);
+        return (!BitVecOps::Equal(traits, mergeOut, m_preMergeOut));
     }
 };
 
@@ -905,8 +916,8 @@ void Compiler::optValnumCSE_DataFlow()
         for (BasicBlock* block = fgFirstBB; block; block = block->bbNext)
         {
             printf("BB%02u", block->bbNum);
-            printf(" cseIn  = %s", genES2str(block->bbCseIn));
-            printf(" cseOut = %s", genES2str(block->bbCseOut));
+            printf(" cseIn  = %s", genES2str(cseTraits, block->bbCseIn));
+            printf(" cseOut = %s", genES2str(cseTraits, block->bbCseOut));
             printf("\n");
         }
 
@@ -946,7 +957,7 @@ void Compiler::optValnumCSE_Availablity()
 
         compCurBB = block;
 
-        EXPSET_TP available_cses = block->bbCseIn;
+        EXPSET_TP available_cses = BitVecOps::MakeCopy(cseTraits, block->bbCseIn);
 
         optCSEweight = block->getBBWeight(this);
 
@@ -961,13 +972,13 @@ void Compiler::optValnumCSE_Availablity()
             {
                 if (IS_CSE_INDEX(tree->gtCSEnum))
                 {
-                    EXPSET_TP mask = genCSEnum2bit(tree->gtCSEnum);
-                    CSEdsc*   desc = optCSEfindDsc(tree->gtCSEnum);
-                    unsigned  stmw = block->getBBWeight(this);
+                    unsigned int cseBit = genCSEnum2bit(tree->gtCSEnum);
+                    CSEdsc*      desc   = optCSEfindDsc(tree->gtCSEnum);
+                    unsigned     stmw   = block->getBBWeight(this);
 
                     /* Is this expression available here? */
 
-                    if (available_cses & mask)
+                    if (BitVecOps::IsMember(cseTraits, available_cses, cseBit))
                     {
                         /* This is a CSE use */
 
@@ -993,8 +1004,7 @@ void Compiler::optValnumCSE_Availablity()
                         tree->gtCSEnum = TO_CSE_DEF(tree->gtCSEnum);
 
                         /* This CSE will be available after this def */
-
-                        available_cses |= mask;
+                        BitVecOps::AddElemD(cseTraits, available_cses, cseBit);
                     }
 #ifdef DEBUG
                     if (verbose && IS_CSE_INDEX(tree->gtCSEnum))
@@ -1236,6 +1246,7 @@ public:
         {
             printf("\nSorted CSE candidates:\n");
             /* Print out the CSE candidates */
+            EXPSET_TP tempMask;
             for (unsigned cnt = 0; cnt < m_pCompiler->optCSECandidateCount; cnt++)
             {
                 Compiler::CSEdsc* dsc  = sortTab[cnt];
@@ -1255,8 +1266,9 @@ public:
                     use = dsc->csdUseWtCnt; // weighted use count (excluding the implicit uses at defs)
                 }
 
+                tempMask = BitVecOps::MakeSingleton(m_pCompiler->cseTraits, genCSEnum2bit(dsc->csdIndex));
                 printf("CSE #%02u,cseMask=%s,useCnt=%d: [def=%3u, use=%3u", dsc->csdIndex,
-                       genES2str(genCSEnum2bit(dsc->csdIndex)), dsc->csdUseCount, def, use);
+                       genES2str(m_pCompiler->cseTraits, tempMask), dsc->csdUseCount, def, use);
                 printf("] :: ");
                 m_pCompiler->gtDispTree(expr, nullptr, nullptr, true);
             }
index 1970313..199f641 100644 (file)
@@ -665,11 +665,9 @@ void dumpILRange(const BYTE* const codeAddr, unsigned codeSize) // in bytes
 
 /*****************************************************************************
  *
- *  Display a variable set (which may be a 32-bit or 64-bit number); only
- *  one or two of these can be used at once.
+ *  Display a variable set.
  */
-
-const char* genES2str(EXPSET_TP set)
+const char* genES2str(BitVecTraits* traits, EXPSET_TP set)
 {
     const int   bufSize = 17;
     static char num1[bufSize];
@@ -682,11 +680,7 @@ const char* genES2str(EXPSET_TP set)
 
     nump = (nump == num1) ? num2 : num1;
 
-#if EXPSET_SZ == 32
-    sprintf_s(temp, bufSize, "%08X", set);
-#else
-    sprintf_s(temp, bufSize, "%08X%08X", (int)(set >> 32), (int)set);
-#endif
+    sprintf_s(temp, bufSize, "%s", BitVecOps::ToString(traits, set));
 
     return temp;
 }