Full support for exception sets in value numbering.
authorBrian Sullivan <briansul@microsoft.com>
Tue, 25 Sep 2018 20:06:24 +0000 (13:06 -0700)
committerBrian Sullivan <briansul@microsoft.com>
Fri, 5 Oct 2018 22:08:49 +0000 (15:08 -0700)
New method that add exception sets:
  fgValueNumberAddExceptionSet
  - fgValueNumberAddExceptionSetForIndirection
  - fgValueNumberAddExceptionSetForDivision
  - fgValueNumberAddExceptionSetForOverflow
  - fgValueNumberAddExceptionSetForCkFinite

Refactoring work added methods:
 VNEvalShouldFold - method to decide if constant folding should be performed
 EvalUsingMathIdentity - Uses math identities to simplify value number exoressions
 Renamed fgValueNumberHelperMethVNFunc to fgValueNumberJitHelperMethodVNFunc

Removed the suffixes from the method headers comments

src/jit/compiler.h
src/jit/optcse.cpp
src/jit/valuenum.cpp
src/jit/valuenum.h
src/jit/valuenumfuncs.h

index 920ae9a..f0e358f 100644 (file)
@@ -4265,8 +4265,24 @@ public:
     // the call may modify the heap (we assume arbitrary memory side effects if so).
     bool fgValueNumberHelperCall(GenTreeCall* helpCall);
 
-    // Requires "helpFunc" to be pure.  Returns the corresponding VNFunc.
-    VNFunc fgValueNumberHelperMethVNFunc(CorInfoHelpFunc helpFunc);
+    // Requires that "helpFunc" is one of the pure Jit Helper methods.
+    // Returns the corresponding VNFunc to use for value numbering
+    VNFunc fgValueNumberJitHelperMethodVNFunc(CorInfoHelpFunc helpFunc);
+
+    // Adds the exception set for the current tree node which is performing a memory indirection operation
+    void fgValueNumberAddExceptionSetForIndirection(GenTree* tree);
+
+    // Adds the exception sets for the current tree node which is performing a division or modulus operation
+    void fgValueNumberAddExceptionSetForDivision(GenTree* tree);
+
+    // Adds the exception set for the current tree node which is performing a overflow checking operation
+    void fgValueNumberAddExceptionSetForOverflow(GenTree* tree);
+
+    // Adds the exception set for the current tree node which is performing a ckfinite operation
+    void fgValueNumberAddExceptionSetForCkFinite(GenTree* tree);
+
+    // Adds the exception sets for the current tree node
+    void fgValueNumberAddExceptionSet(GenTree* tree);
 
     // These are the current value number for the memory implicit variables while
     // doing value numbering.  These are the value numbers under the "liberal" interpretation
@@ -5760,7 +5776,7 @@ protected:
     {
         CSEdsc* csdNextInBucket; // used by the hash table
 
-        unsigned csdHashValue; // the orginal hashkey
+        unsigned csdHashKey; // the orginal hashkey
 
         unsigned csdIndex;          // 1..optCSECandidateCount
         char     csdLiveAcrossCall; // 0 or 1
@@ -5778,8 +5794,13 @@ protected:
         treeStmtLst* csdTreeList; // list of matching tree nodes: head
         treeStmtLst* csdTreeLast; // list of matching tree nodes: tail
 
-        ValueNum defConservativeVN; // if all def occurrences share the same conservative value
-                                    // number, this will reflect it; otherwise, NoVN.
+        ValueNum defExcSetPromise; // The exception set that is now required for all defs of this CSE.
+                                   // This will be set to NoVN if we decide to abandon this CSE
+
+        ValueNum defExcSetCurrent; // The set of exceptions we currently can use for CSE uses.
+
+        ValueNum defConservNormVN; // if all def occurrences share the same conservative normal value
+                                   // number, this will reflect it; otherwise, NoVN.
     };
 
     static const size_t s_optCSEhashSize;
index 7b87748..ff2353a 100644 (file)
@@ -390,11 +390,62 @@ unsigned Compiler::optValnumCSE_Index(GenTree* tree, GenTree* stmt)
     unsigned hval;
     CSEdsc*  hashDsc;
 
-    ValueNum vnlib = tree->GetVN(VNK_Liberal);
+    // We use the liberal Value numbers when building the set of CSE
+    ValueNum vnLib = tree->GetVN(VNK_Liberal);
+
+    // We usually want to remove the exception sets by using the normal value
+    // since a GT_IND will often have a NullPtrExc entry in its exc set, but
+    // sometimes we have cleared the GTF_EXCEPT flag, or we may have assigned
+    // the value into a LCL_VAR.  In the general case we want to have the CSE
+    // candidates that contain both, so we will nearly always use the normal
+    // value number as the CSE Key
+    //
+    // Later on when we are promoting the CSE candidates we insure that all of the
+    // CSE defs have the same exception set.  Any CSE uses that we promote
+    // must have an exc set that is the same as the CSE defs or have an empty set.
+    // (alternatively we could use a subset operation on the exc sets)
+    //
+    // One exception to using the normal value is for the GT_COMMA nodes.
+    // So we check to see if we have a GT_COMMA with a different value number
+    // than the one from its op2.  For this case we create two CSE candidates.
+    // This allows us to CSE the GT_COMMA separately from its value.
+    //
+    ValueNum vnLibNorm = vnStore->VNNormalValue(vnLib);
+
+    // We assign either vnLib or vnLibNorm as the hash key
+    if (tree->OperGet() == GT_COMMA)
+    {
+        // op2 is the value produced by a GT_COMMA
+        GenTree* op2      = tree->gtOp.gtOp2;
+        ValueNum vnOp2Lib = op2->GetVN(VNK_Liberal);
 
-    /* Compute the hash value for the expression */
+        // If the value number for op2 and tree are different
+        // then some new exceptions were produced by op1.
+        // For that case we will NOT use the normal value
+        // This allows us to CSE commas with an op1 that is
+        // an ARR_BOUNDS_CHECK.
 
-    key = (unsigned)vnlib;
+        if (vnOp2Lib != vnLib)
+        {
+            key = (unsigned)vnLib; // include the exc set in the hash key
+        }
+        else
+        {
+            key = (unsigned)vnLibNorm;
+        }
+
+        // If we didn't do the above we would have op1 as the CSE def
+        // and the parent comma as the CSE use (but with a different exc set)
+        // This would prevent us from making any CSE with the comma
+        //
+        assert(vnLibNorm == vnStore->VNNormalValue(vnOp2Lib));
+    }
+    else // Not a GT_COMMA
+    {
+        key = (unsigned)vnLibNorm;
+    }
+
+    // Compute the hash value for the expression
 
     hash = key;
     hash *= (unsigned)(s_optCSEhashSize + 1);
@@ -408,7 +459,7 @@ unsigned Compiler::optValnumCSE_Index(GenTree* tree, GenTree* stmt)
 
     for (hashDsc = optCSEhash[hval]; hashDsc; hashDsc = hashDsc->csdNextInBucket)
     {
-        if (hashDsc->csdHashValue == key)
+        if (hashDsc->csdHashKey == key)
         {
             treeStmtLst* newElem;
 
@@ -453,10 +504,6 @@ unsigned Compiler::optValnumCSE_Index(GenTree* tree, GenTree* stmt)
                 newCSE = true;
                 break;
             }
-#if 0 
-            // Use this to see if this Value Number base CSE is also a lexical CSE
-            bool treeMatch = GenTree::Compare(hashDsc->csdTree, tree, true);
-#endif
 
             assert(FitsIn<signed char>(hashDsc->csdIndex));
             tree->gtCSEnum = ((signed char)hashDsc->csdIndex);
@@ -472,13 +519,16 @@ unsigned Compiler::optValnumCSE_Index(GenTree* tree, GenTree* stmt)
         {
             hashDsc = new (this, CMK_CSE) CSEdsc;
 
-            hashDsc->csdHashValue      = key;
+            hashDsc->csdHashKey        = key;
             hashDsc->csdIndex          = 0;
             hashDsc->csdLiveAcrossCall = 0;
             hashDsc->csdDefCount       = 0;
             hashDsc->csdUseCount       = 0;
             hashDsc->csdDefWtCnt       = 0;
             hashDsc->csdUseWtCnt       = 0;
+            hashDsc->defExcSetPromise  = vnStore->VNForEmptyExcSet();
+            hashDsc->defExcSetCurrent  = vnStore->VNForNull(); // uninit value
+            hashDsc->defConservNormVN  = vnStore->VNForNull(); // uninit value
 
             hashDsc->csdTree     = tree;
             hashDsc->csdStmt     = stmt;
@@ -525,7 +575,7 @@ unsigned Compiler::optValnumCSE_Index(GenTree* tree, GenTree* stmt)
         {
             EXPSET_TP tempMask = BitVecOps::MakeSingleton(cseTraits, genCSEnum2bit(CSEindex));
             printf("\nCSE candidate #%02u, vn=", CSEindex);
-            vnPrint(vnlib, 0);
+            vnPrint(key, 0);
             printf(" cseMask=%s in " FMT_BB ", [cost=%2u, size=%2u]: \n", genES2str(cseTraits, tempMask),
                    compCurBB->bbNum, tree->gtCostEx, tree->gtCostSz);
             gtDispTree(tree);
@@ -582,9 +632,7 @@ unsigned Compiler::optValnumCSE_Locate()
                     continue;
                 }
 
-                ValueNum vnlib = tree->GetVN(VNK_Liberal);
-
-                if (ValueNumStore::isReservedVN(vnlib))
+                if (ValueNumStore::isReservedVN(tree->GetVN(VNK_Liberal)))
                 {
                     continue;
                 }
@@ -597,7 +645,7 @@ unsigned Compiler::optValnumCSE_Locate()
                 // and the point is to avoid optimizing cases that it will
                 // handle.
                 //
-                if (vnStore->IsVNConstant(tree->GetVN(VNK_Conservative)))
+                if (vnStore->IsVNConstant(vnStore->VNConservativeNormalValue(tree->gtVNPair)))
                 {
                     continue;
                 }
@@ -920,79 +968,251 @@ void Compiler::optValnumCSE_Availablity()
         GenTree* stmt;
         GenTree* tree;
 
-        /* Make the block publicly available */
+        // Make the block publicly available
 
         compCurBB = block;
 
+        // Retrieve the available CSE's at the start of this block
+
         BitVecOps::Assign(cseTraits, available_cses, block->bbCseIn);
 
         optCSEweight = block->getBBWeight(this);
 
-        /* Walk the statement trees in this basic block */
+        // Walk the statement trees in this basic block
 
         for (stmt = block->FirstNonPhiDef(); stmt; stmt = stmt->gtNext)
         {
             noway_assert(stmt->gtOper == GT_STMT);
 
-            /* We walk the tree in the forwards direction (bottom up) */
+            // We walk the tree in the forwards direction (bottom up)
+
             for (tree = stmt->gtStmt.gtStmtList; tree; tree = tree->gtNext)
             {
                 if (IS_CSE_INDEX(tree->gtCSEnum))
                 {
-                    unsigned int cseBit = genCSEnum2bit(tree->gtCSEnum);
-                    CSEdsc*      desc   = optCSEfindDsc(tree->gtCSEnum);
+                    unsigned     CSEnum = GET_CSE_INDEX(tree->gtCSEnum);
+                    unsigned int cseBit = genCSEnum2bit(CSEnum);
+                    CSEdsc*      desc   = optCSEfindDsc(CSEnum);
                     unsigned     stmw   = block->getBBWeight(this);
+                    bool         isUse  = BitVecOps::IsMember(cseTraits, available_cses, cseBit);
+                    bool         isDef  = !isUse; // If is isn't a CSE use, it is a CSE def
+#ifdef DEBUG
+                    VNFuncApp excSeq;
 
-                    /* Is this expression available here? */
+                    if (verbose)
+                    {
+                        printf("BB%02u ", block->bbNum);
+                        printTreeID(tree);
 
-                    if (BitVecOps::IsMember(cseTraits, available_cses, cseBit))
+                        printf(" %s of CSE #%02u [weight=%s]\n", isUse ? "Use" : "Def", CSEnum, refCntWtd2str(stmw));
+                    }
+#endif
+                    // Have we decided to abandon work on this CSE?
+                    if (desc->defExcSetPromise == ValueNumStore::NoVN)
                     {
-                        /* This is a CSE use */
+                        // This candidate had defs with differing liberal exc set VNs
+                        // We have abandoned CSE promotion for this candidate
 
-                        desc->csdUseCount += 1;
-                        desc->csdUseWtCnt += stmw;
+                        // Clear the CSE flag
+                        tree->gtCSEnum = NO_CSE;
+
+                        JITDUMP(" Abandoned - CSE candidate has defs with different exception sets!\n");
+                        continue;
                     }
-                    else
+
+                    // Record the exception set for tree's liberal value number
+                    //
+                    ValueNum theLiberalExcSet = vnStore->VNExceptionSet(tree->gtVNPair.GetLiberal());
+
+                    // Is this a CSE use or a def?
+
+                    if (isDef)
                     {
+                        // @ToDo - Remove this block as it no longer applies
                         if (tree->gtFlags & GTF_COLON_COND)
                         {
                             // We can't create CSE definitions inside QMARK-COLON trees
                             tree->gtCSEnum = NO_CSE;
+
+                            JITDUMP(" NO_CSE - This CSE def occurs in a GTF_COLON_COND!\n");
                             continue;
                         }
 
-                        /* This is a CSE def */
+                        // This is a CSE def
+
+                        // Is defExcSetCurrent still set to the uninit marker value of VNForNull() ?
+                        if (desc->defExcSetCurrent == vnStore->VNForNull())
+                        {
+                            // This is the first time visited, so record this defs exeception set
+                            desc->defExcSetCurrent = theLiberalExcSet;
+                        }
+
+                        // Have we seen a CSE use and made a promise of an exception set?
+                        //
+                        if (desc->defExcSetPromise != vnStore->VNForEmptyExcSet())
+                        {
+                            // The exeception set held in desc->defExcSetPromise must be a subset of theLiberalExcSet
+                            //
+                            if (vnStore->VNExcIsSubset(theLiberalExcSet, desc->defExcSetPromise))
+                            {
+                                // This new def still satisfies any promise made to all the CSE uses that we have
+                                // encountered
+                                //
+
+                                // no update is needed when these are the same VN
+                                if (desc->defExcSetCurrent != theLiberalExcSet)
+                                {
+                                    // We will change the value of desc->defExcSetCurrent to be the intersection of
+                                    // these two sets.
+                                    // This is the set of exceptions that all CSE defs have (that we have visted so far)
+                                    //
+                                    ValueNum intersectionExcSet =
+                                        vnStore->VNExcSetIntersection(desc->defExcSetCurrent, theLiberalExcSet);
+#ifdef DEBUG
+                                    if (this->verbose)
+                                    {
+                                        vnStore->GetVNFunc(desc->defExcSetCurrent, &excSeq);
+                                        printf(">>> defExcSetCurrent is ");
+                                        vnStore->vnDumpExcSeq(this, &excSeq, true);
+                                        printf("\n");
+
+                                        vnStore->GetVNFunc(theLiberalExcSet, &excSeq);
+                                        printf(">>> theLiberalExcSet is ");
+                                        vnStore->vnDumpExcSeq(this, &excSeq, true);
+                                        printf("\n");
+
+                                        if (intersectionExcSet == vnStore->VNForEmptyExcSet())
+                                        {
+                                            printf(">>> the intersectionExcSet is the EmptyExcSet\n");
+                                        }
+                                        else
+                                        {
+                                            vnStore->GetVNFunc(intersectionExcSet, &excSeq);
+                                            printf(">>> the intersectionExcSet is ");
+                                            vnStore->vnDumpExcSeq(this, &excSeq, true);
+                                            printf("\n");
+                                        }
+                                    }
+#endif // DEBUG
+                                    // Change the defExcSetCurrent to be a subset of its prior value
+                                    //
+                                    assert(vnStore->VNExcIsSubset(desc->defExcSetCurrent, intersectionExcSet));
+                                    desc->defExcSetCurrent = intersectionExcSet;
+                                }
+                            }
+                            else // This CSE def doesn't satisfy one of the exceptions already promised to a CSE use
+                            {
+                                // So, we will abandon all CSE promotions for this candidate
+                                //
+                                // We use the marker value of NoVN to indicate that we
+                                // should abandon this CSE candidate
+                                //
+                                desc->defExcSetPromise = ValueNumStore::NoVN;
+                                tree->gtCSEnum         = NO_CSE;
+
+                                JITDUMP(" Abandon - CSE candidate has defs with exception sets that do not satisfy "
+                                        "some CSE use\n");
+                                continue;
+                            }
+                        }
+
+                        // Record or update the value of desc->defConservNormVN
+                        //
+                        ValueNum theConservNormVN = vnStore->VNConservativeNormalValue(tree->gtVNPair);
 
-                        if (desc->csdDefCount == 0)
+                        // Is defConservNormVN still set to the uninit marker value of VNForNull() ?
+                        if (desc->defConservNormVN == vnStore->VNForNull())
                         {
-                            // This is the first def visited, so copy its conservative VN
-                            desc->defConservativeVN = tree->gtVNPair.GetConservative();
+                            // This is the first def that we have visited, set defConservNormVN
+                            desc->defConservNormVN = theConservNormVN;
                         }
-                        else if (tree->gtVNPair.GetConservative() != desc->defConservativeVN)
+                        else
                         {
-                            // This candidate has defs with differing conservative VNs
-                            desc->defConservativeVN = ValueNumStore::NoVN;
+                            // Check to see if all defs have the same conservative normal VN
+                            if (theConservNormVN != desc->defConservNormVN)
+                            {
+                                // This candidate has defs with differing conservative normal VNs, mark it with NoVN
+                                desc->defConservNormVN = ValueNumStore::NoVN; // record the marker for differing VNs
+                            }
                         }
 
+                        // If we get here we have accepted this node as a valid CSE def
+
                         desc->csdDefCount += 1;
                         desc->csdDefWtCnt += stmw;
 
-                        /* Mark the node as a CSE definition */
+                        // Mark the node as a CSE definition
 
                         tree->gtCSEnum = TO_CSE_DEF(tree->gtCSEnum);
 
-                        /* This CSE will be available after this def */
+                        // This CSE becomes available after this def
                         BitVecOps::AddElemD(cseTraits, available_cses, cseBit);
                     }
-#ifdef DEBUG
-                    if (verbose && IS_CSE_INDEX(tree->gtCSEnum))
+                    else // We are visiting a CSE use
                     {
-                        printf(FMT_BB " ", block->bbNum);
-                        printTreeID(tree);
-                        printf(" %s of CSE #%02u [weight=%s]\n", IS_CSE_USE(tree->gtCSEnum) ? "Use" : "Def",
-                               GET_CSE_INDEX(tree->gtCSEnum), refCntWtd2str(stmw));
+                        assert(isUse);
+
+                        // If the CSE use has no requirements for an exception set then we don't have to do anything
+                        // here
+                        //
+                        if (theLiberalExcSet != vnStore->VNForEmptyExcSet())
+                        {
+                            // Are we visiting a use first, before visiting any defs of this CSE?
+                            // This is an atypical case that can occur with a bottom tested loop.
+                            //
+                            // Is defExcSetCurrent still set to the uninit marker value of VNForNull() ?
+                            if (desc->defExcSetCurrent == vnStore->VNForNull())
+                            {
+                                // Update defExcSetPromise, this is our required exception set for all CSE defs
+                                // that we encounter later.
+                                //
+                                // We could see multiple uses before a def, so we require the Union of all exception
+                                // sets
+                                //
+                                desc->defExcSetPromise =
+                                    vnStore->VNExcSetUnion(desc->defExcSetPromise, theLiberalExcSet);
+                            }
+                            else // we have already seen a def for this CSE and defExcSetCurrent is setup
+                            {
+                                if (vnStore->VNExcIsSubset(desc->defExcSetCurrent, theLiberalExcSet))
+                                {
+                                    // The current set of exceptions produced by all CSE defs have (that we have visted
+                                    // so far)
+                                    // meets our requirement
+                                    //
+                                    // Add any exception items to the defExcSetPromise set
+                                    //
+                                    desc->defExcSetPromise =
+                                        vnStore->VNExcSetUnion(desc->defExcSetPromise, theLiberalExcSet);
+                                }
+                            }
+
+                            // At this point defExcSetPromise contains all of the exception items that we can promise
+                            // here.
+                            //
+                            if (!vnStore->VNExcIsSubset(desc->defExcSetPromise, theLiberalExcSet))
+                            {
+                                // We can't safely make this into a CSE use, because this
+                                // CSE use has an exeception set item that is not promised
+                                // by all of our CSE defs.
+                                //
+                                // We will omit this CSE use from the graph and proceed,
+                                // the other uses and defs can still participate in the CSE optimization.
+
+                                // So this can't be a CSE use
+                                tree->gtCSEnum = NO_CSE;
+
+                                JITDUMP(
+                                    " NO_CSE - This use has an exception set item that isn't contained in the defs!\n");
+                                continue;
+                            }
+                        }
+
+                        // When we get here we have accepted this node as a valid CSE use
+
+                        desc->csdUseCount += 1;
+                        desc->csdUseWtCnt += stmw;
                     }
-#endif
                 }
             }
         }
@@ -1257,8 +1477,9 @@ public:
                 }
 
                 tempMask = BitVecOps::MakeSingleton(m_pCompiler->cseTraits, genCSEnum2bit(dsc->csdIndex));
-                printf("CSE #%02u,cseMask=%s,useCnt=%d: [def=%3u, use=%3u", dsc->csdIndex,
-                       genES2str(m_pCompiler->cseTraits, tempMask), dsc->csdUseCount, def, use);
+                printf("CSE #%02u, {$%-3x, $%-3x} cseMask=%s,useCnt=%d: [def=%3u, use=%3u", dsc->csdIndex,
+                       dsc->csdHashKey, dsc->defExcSetPromise, genES2str(m_pCompiler->cseTraits, tempMask),
+                       dsc->csdUseCount, def, use);
                 printf("] :: ");
                 m_pCompiler->gtDispTree(expr, nullptr, nullptr, true);
             }
@@ -1754,18 +1975,16 @@ public:
         m_pCompiler->lvaTable[cseLclVarNum].lvType  = cseLclVarTyp;
         m_pCompiler->lvaTable[cseLclVarNum].lvIsCSE = true;
 
-        m_addCSEcount++; // Record that we created a new LclVar for use as a CSE temp
+        // Record that we created a new LclVar for use as a CSE temp
+        m_addCSEcount++;
         m_pCompiler->optCSEcount++;
 
-        ValueNum defConservativeVN = successfulCandidate->CseDsc()->defConservativeVN;
-
-        /*  Walk all references to this CSE, adding an assignment
-            to the CSE temp to all defs and changing all refs to
-            a simple use of the CSE temp.
-
-            We also unmark nested CSE's for all uses.
-        */
-
+        //  Walk all references to this CSE, adding an assignment
+        //  to the CSE temp to all defs and changing all refs to
+        //  a simple use of the CSE temp.
+        //
+        //  We also unmark nested CSE's for all uses.
+        //
         Compiler::treeStmtLst* lst;
         lst = successfulCandidate->CseDsc()->csdTreeList;
         noway_assert(lst);
@@ -1859,22 +2078,27 @@ public:
                 // We will replace the CSE ref with a new tree
                 // this is typically just a simple use of the new CSE LclVar
                 //
-                cse           = m_pCompiler->gtNewLclvNode(cseLclVarNum, cseLclVarTyp);
-                cse->gtVNPair = exp->gtVNPair; // assign the proper Value Numbers
-                if (defConservativeVN != ValueNumStore::NoVN)
+                ValueNumStore* vnStore = m_pCompiler->vnStore;
+                cse                    = m_pCompiler->gtNewLclvNode(cseLclVarNum, cseLclVarTyp);
+
+                // assign the proper ValueNumber, A CSE use discards any exceptions
+                cse->gtVNPair = vnStore->VNPNormalPair(exp->gtVNPair);
+
+                ValueNum theConservativeVN = successfulCandidate->CseDsc()->defConservNormVN;
+
+                if (theConservativeVN != ValueNumStore::NoVN)
                 {
-                    // All defs of this CSE share the same conservative VN, and we are rewriting this
+                    // All defs of this CSE share the same normal conservative VN, and we are rewriting this
                     // use to fetch the same value with no reload, so we can safely propagate that
                     // conservative VN to this use.  This can help range check elimination later on.
-                    cse->gtVNPair.SetConservative(defConservativeVN);
+                    cse->gtVNPair.SetConservative(theConservativeVN);
 
                     // If the old VN was flagged as a checked bound, propagate that to the new VN
                     // to make sure assertion prop will pay attention to this VN.
-                    ValueNumStore* vnStore = m_pCompiler->vnStore;
-                    ValueNum       oldVN   = exp->gtVNPair.GetConservative();
-                    if (!vnStore->IsVNConstant(defConservativeVN) && vnStore->IsVNCheckedBound(oldVN))
+                    ValueNum oldVN = exp->gtVNPair.GetConservative();
+                    if (!vnStore->IsVNConstant(theConservativeVN) && vnStore->IsVNCheckedBound(oldVN))
                     {
-                        vnStore->SetVNIsCheckedBound(defConservativeVN);
+                        vnStore->SetVNIsCheckedBound(theConservativeVN);
                     }
 
                     GenTree* cmp;
@@ -1893,7 +2117,7 @@ public:
                         {
                             // Comparison is against the bound directly.
 
-                            newCmpArgVN = defConservativeVN;
+                            newCmpArgVN = theConservativeVN;
                             vnStore->GetCompareCheckedBound(oldCmpVN, &info);
                         }
                         else
@@ -1903,7 +2127,7 @@ public:
                             assert(vnStore->IsVNCompareCheckedBoundArith(oldCmpVN));
                             vnStore->GetCompareCheckedBoundArithInfo(oldCmpVN, &info);
                             newCmpArgVN = vnStore->VNForFunc(vnStore->TypeOfVN(info.arrOp), (VNFunc)info.arrOper,
-                                                             info.arrOp, defConservativeVN);
+                                                             info.arrOp, theConservativeVN);
                         }
                         ValueNum newCmpVN = vnStore->VNForFunc(vnStore->TypeOfVN(oldCmpVN), (VNFunc)info.cmpOper,
                                                                info.cmpOp, newCmpArgVN);
@@ -1925,6 +2149,7 @@ public:
 
                 GenTree* sideEffList = nullptr;
                 m_pCompiler->gtExtractSideEffList(exp, &sideEffList, GTF_PERSISTENT_SIDE_EFFECTS | GTF_IS_IN_CSE);
+
                 // If we have any side effects or extracted CSE defs then we need to create a GT_COMMA tree instead
                 //
                 if (sideEffList != nullptr)
@@ -2077,26 +2302,28 @@ public:
         for (; (cnt > 0); cnt--, ptr++)
         {
             Compiler::CSEdsc* dsc = *ptr;
-            CSE_Candidate     candidate(this, dsc);
+            if (dsc->defExcSetPromise == ValueNumStore::NoVN)
+            {
+                JITDUMP("Abandoned CSE #%02u because we had defs with different Exc sets\n");
+                continue;
+            }
+
+            CSE_Candidate candidate(this, dsc);
 
             candidate.InitializeCounts();
 
             if (candidate.UseCount() == 0)
             {
-#ifdef DEBUG
-                if (m_pCompiler->verbose)
-                {
-                    printf("Skipped CSE #%02u because use count is 0\n", candidate.CseIndex());
-                }
-#endif
+                JITDUMP("Skipped CSE #%02u because use count is 0\n", candidate.CseIndex());
                 continue;
             }
 
 #ifdef DEBUG
             if (m_pCompiler->verbose)
             {
-                printf("\nConsidering CSE #%02u [def=%2u, use=%2u, cost=%2u] CSE Expression:\n", candidate.CseIndex(),
-                       candidate.DefCount(), candidate.UseCount(), candidate.Cost());
+                printf("\nConsidering CSE #%02u {$%-3x, $%-3x} [def=%2u, use=%2u, cost=%2u] CSE Expression:\n",
+                       candidate.CseIndex(), dsc->csdHashKey, dsc->defExcSetPromise, candidate.DefCount(),
+                       candidate.UseCount(), candidate.Cost());
                 m_pCompiler->gtDispTree(candidate.Expr());
                 printf("\n");
             }
index c1559e9..edd33c8 100644 (file)
@@ -1801,69 +1801,97 @@ ValueNum ValueNumStore::VNOneForType(var_types typ)
 
 class Object* ValueNumStore::s_specialRefConsts[] = {nullptr, nullptr, nullptr};
 
-// Nullary operators (i.e., symbolic constants).
+//----------------------------------------------------------------------------------------
+//  VNForFunc  - Returns the ValueNum associated with 'func'
+//               There is a one-to-one relationship between the ValueNum and 'func'
+//
+// Arguments:
+//    typ            - The type of the resulting ValueNum produced by 'func'
+//    func           - Any nullary VNFunc
+//
+// Return Value:     - Returns the ValueNum associated with 'func'
+//
+// Note: - This method only handles Nullary operators (i.e., symbolic constants).
+//
 ValueNum ValueNumStore::VNForFunc(var_types typ, VNFunc func)
 {
     assert(VNFuncArity(func) == 0);
     assert(func != VNF_NotAField);
 
-    ValueNum res;
+    ValueNum resultVN;
 
-    if (GetVNFunc0Map()->Lookup(func, &res))
-    {
-        return res;
-    }
-    else
+    // Have we already assigned a ValueNum for 'func' ?
+    //
+    if (!GetVNFunc0Map()->Lookup(func, &resultVN))
     {
+        // Allocate a new ValueNum for 'func'
         Chunk*   c                                              = GetAllocChunk(typ, CEA_Func0);
         unsigned offsetWithinChunk                              = c->AllocVN();
-        res                                                     = c->m_baseVN + offsetWithinChunk;
+        resultVN                                                = c->m_baseVN + offsetWithinChunk;
         reinterpret_cast<VNFunc*>(c->m_defs)[offsetWithinChunk] = func;
-        GetVNFunc0Map()->Set(func, res);
-        return res;
+        GetVNFunc0Map()->Set(func, resultVN);
     }
+    return resultVN;
 }
 
+//----------------------------------------------------------------------------------------
+//  VNForFunc  - Returns the ValueNum associated with 'func'('arg0VN')
+//               There is a one-to-one relationship between the ValueNum
+//               and 'func'('arg0VN')
+//
+// Arguments:
+//    typ            - The type of the resulting ValueNum produced by 'func'
+//    func           - Any unary VNFunc
+//    arg0VN         - The ValueNum of the argument to 'func'
+//
+// Return Value:     - Returns the ValueNum associated with 'func'('arg0VN')
+//
+// Note: - This method only handles Unary operators
+//
 ValueNum ValueNumStore::VNForFunc(var_types typ, VNFunc func, ValueNum arg0VN)
 {
     assert(arg0VN == VNNormalValue(arg0VN)); // Arguments don't carry exceptions.
 
-    ValueNum      res;
-    VNDefFunc1Arg fstruct(func, arg0VN);
-
-    // Do constant-folding.
+    // Try to perform constant-folding.
     if (CanEvalForConstantArgs(func) && IsVNConstant(arg0VN))
     {
         return EvalFuncForConstantArgs(typ, func, arg0VN);
     }
 
-    if (GetVNFunc1Map()->Lookup(fstruct, &res))
-    {
-        return res;
-    }
-    else
+    ValueNum resultVN;
+
+    // Have we already assigned a ValueNum for 'func'('arg0VN') ?
+    //
+    VNDefFunc1Arg fstruct(func, arg0VN);
+    if (!GetVNFunc1Map()->Lookup(fstruct, &resultVN))
     {
-        // Otherwise, create a new VN for this application.
+        // Otherwise, Allocate a new ValueNum for 'func'('arg0VN')
+        //
         Chunk*   c                                                     = GetAllocChunk(typ, CEA_Func1);
         unsigned offsetWithinChunk                                     = c->AllocVN();
-        res                                                            = c->m_baseVN + offsetWithinChunk;
+        resultVN                                                       = c->m_baseVN + offsetWithinChunk;
         reinterpret_cast<VNDefFunc1Arg*>(c->m_defs)[offsetWithinChunk] = fstruct;
-        GetVNFunc1Map()->Set(fstruct, res);
-        return res;
+        // Record 'resultVN' in the Func1Map
+        GetVNFunc1Map()->Set(fstruct, resultVN);
     }
+    return resultVN;
 }
 
-// Windows x86 and Windows ARM/ARM64 may not define _isnanf() but they do define _isnan().
-// We will redirect the macros to these other functions if the macro is not defined for the
-// platform. This has the side effect of a possible implicit upcasting for arguments passed.
-#if (defined(_TARGET_X86_) || defined(_TARGET_ARM_) || defined(_TARGET_ARM64_)) && !defined(FEATURE_PAL)
-
-#if !defined(_isnanf)
-#define _isnanf _isnan
-#endif
-
-#endif
-
+//----------------------------------------------------------------------------------------
+//  VNForFunc  - Returns the ValueNum associated with 'func'('arg0VN','arg1VN')
+//               There is a one-to-one relationship between the ValueNum
+//               and 'func'('arg0VN','arg1VN')
+//
+// Arguments:
+//    typ            - The type of the resulting ValueNum produced by 'func'
+//    func           - Any binary VNFunc
+//    arg0VN         - The ValueNum of the first argument to 'func'
+//    arg1VN         - The ValueNum of the second argument to 'func'
+//
+// Return Value:     - Returns the ValueNum associated with 'func'('arg0VN','arg1VN')
+//
+// Note: - This method only handles Binary operators
+//
 ValueNum ValueNumStore::VNForFunc(var_types typ, VNFunc func, ValueNum arg0VN, ValueNum arg1VN)
 {
     assert(arg0VN != NoVN && arg1VN != NoVN);
@@ -1872,16 +1900,16 @@ ValueNum ValueNumStore::VNForFunc(var_types typ, VNFunc func, ValueNum arg0VN, V
     assert(VNFuncArity(func) == 2);
     assert(func != VNF_MapSelect); // Precondition: use the special function VNForMapSelect defined for that.
 
-    ValueNum res;
+    ValueNum resultVN;
 
-    // When both operands are constants we can usually perform the constant-folding.
+    // When both operands are constants we can usually perform constant-folding.
     //
     if (CanEvalForConstantArgs(func) && IsVNConstant(arg0VN) && IsVNConstant(arg1VN))
     {
         bool canFold = true; // Normally we will be able to fold this 'func'
 
         // Special case for VNF_Cast of constant handles
-        // Don't allow eval/fold of a GT_CAST(non-I_IMPL, Handle)
+        // Don't allow an eval/fold of a GT_CAST(non-I_IMPL, Handle)
         //
         if ((func == VNF_Cast) && (typ != TYP_I_IMPL) && IsVNHandle(arg0VN))
         {
@@ -1889,79 +1917,9 @@ ValueNum ValueNumStore::VNForFunc(var_types typ, VNFunc func, ValueNum arg0VN, V
         }
 
         // Currently CanEvalForConstantArgs() returns false for VNF_CastOvf
-        // IN the future we may want to handle this case.
+        // In the future we could handle this case in folding.
         assert(func != VNF_CastOvf);
 
-        // We have some arithmetic operations that will always throw
-        // an exception given particular constant argument(s).
-        // (i.e. integer division by zero)
-        //
-        // We will avoid performing any constant folding on them
-        // since they won't actually produce any result (because
-        // they instead they always will throw an exception)
-        //
-        if (func < VNF_Boundary)
-        {
-            genTreeOps oper = genTreeOps(func);
-
-            // Floating point operations do not throw exceptions
-            //
-            if (!varTypeIsFloating(typ))
-            {
-                // Is this an integer divide/modulo that will throw an exception?
-                //
-                if ((oper == GT_DIV) || (oper == GT_UDIV) || (oper == GT_MOD) || (oper == GT_UMOD))
-                {
-                    if ((TypeOfVN(arg0VN) != typ) || (TypeOfVN(arg1VN) != typ))
-                    {
-                        // Just in case we have mismatched types
-                        canFold = false;
-                    }
-                    else
-                    {
-                        bool isUnsigned = (oper == GT_UDIV) || (oper == GT_UMOD);
-                        if (typ == TYP_LONG)
-                        {
-                            INT64 kArg0 = ConstantValue<INT64>(arg0VN);
-                            INT64 kArg1 = ConstantValue<INT64>(arg1VN);
-
-                            if (IsIntZero(kArg1))
-                            {
-                                // Don't fold we have a divide by zero
-                                canFold = false;
-                            }
-                            else if (!isUnsigned || IsOverflowIntDiv(kArg0, kArg1))
-                            {
-                                // Don't fold we have a divide of INT64_MIN/-1
-                                canFold = false;
-                            }
-                        }
-                        else if (typ == TYP_INT)
-                        {
-                            int kArg0 = ConstantValue<int>(arg0VN);
-                            int kArg1 = ConstantValue<int>(arg1VN);
-
-                            if (IsIntZero(kArg1))
-                            {
-                                // Don't fold We have a divide by zero
-                                canFold = false;
-                            }
-                            else if (!isUnsigned && IsOverflowIntDiv(kArg0, kArg1))
-                            {
-                                // Don't fold we have a divide of INT32_MIN/-1
-                                canFold = false;
-                            }
-                        }
-                        else // strange value for 'typ'
-                        {
-                            assert(!"unexpected 'typ' in VNForFunc constant folding");
-                            canFold = false;
-                        }
-                    }
-                }
-            }
-        }
-
         // It is possible for us to have mismatched types (see Bug 750863)
         // We don't try to fold a binary operation when one of the constant operands
         // is a floating-point constant and the other is not.
@@ -1988,14 +1946,24 @@ ValueNum ValueNumStore::VNForFunc(var_types typ, VNFunc func, ValueNum arg0VN, V
         {
             canFold = false;
         }
+
         if (typ == TYP_BYREF)
         {
             // We don't want to fold expressions that produce TYP_BYREF
             canFold = false;
         }
 
+        bool shouldFold = canFold;
+
         if (canFold)
         {
+            // We can fold the expression, but we don't want to fold
+            // when the expression will always throw an exception
+            shouldFold = VNEvalShouldFold(typ, func, arg0VN, arg1VN);
+        }
+
+        if (shouldFold)
+        {
             return EvalFuncForConstantArgs(typ, func, arg0VN, arg1VN);
         }
     }
@@ -2009,216 +1977,145 @@ ValueNum ValueNumStore::VNForFunc(var_types typ, VNFunc func, ValueNum arg0VN, V
             jitstd::swap(arg0VN, arg1VN);
         }
     }
+
+    // Have we already assigned a ValueNum for 'func'('arg0VN','arg1VN') ?
+    //
     VNDefFunc2Arg fstruct(func, arg0VN, arg1VN);
-    if (GetVNFunc2Map()->Lookup(fstruct, &res))
+    if (!GetVNFunc2Map()->Lookup(fstruct, &resultVN))
     {
-        return res;
-    }
-    else
-    {
-        // We have ways of evaluating some binary functions.
-        if (func < VNF_Boundary)
+        if (func == VNF_CastClass)
         {
-            if (typ != TYP_BYREF) // We don't want/need to optimize a zero byref
-            {
-                ValueNum resultVN = NoVN;
-                ValueNum ZeroVN, OneVN; // We may need to create one of these in the switch below.
-                switch (genTreeOps(func))
-                {
-                    case GT_ADD:
-                        // This identity does not apply for floating point (when x == -0.0)
-                        // (x + 0) == (0 + x) => x
-                        ZeroVN = VNZeroForType(typ);
-                        if (VNIsEqual(arg0VN, ZeroVN))
-                        {
-                            resultVN = arg1VN;
-                        }
-                        else if (VNIsEqual(arg1VN, ZeroVN))
-                        {
-                            resultVN = arg0VN;
-                        }
-                        break;
-
-                    case GT_SUB:
-                        // This identity does not apply for floating point (when x == -0.0)
-                        // (x - 0) => x
-                        // (x - x) => 0
-                        ZeroVN = VNZeroForType(typ);
-                        if (VNIsEqual(arg1VN, ZeroVN))
-                        {
-                            resultVN = arg0VN;
-                        }
-                        else if (VNIsEqual(arg0VN, arg1VN))
-                        {
-                            resultVN = ZeroVN;
-                        }
-                        break;
-
-                    case GT_MUL:
-                        // (x * 1) == (1 * x) => x
-                        OneVN = VNOneForType(typ);
-                        if (OneVN != NoVN)
-                        {
-                            if (arg0VN == OneVN)
-                            {
-                                resultVN = arg1VN;
-                            }
-                            else if (arg1VN == OneVN)
-                            {
-                                resultVN = arg0VN;
-                            }
-                        }
-
-                        if (!varTypeIsFloating(typ))
-                        {
-                            // (x * 0) == (0 * x) => 0 (unless x is NaN, which we must assume a fp value may be)
-                            ZeroVN = VNZeroForType(typ);
-                            if (arg0VN == ZeroVN)
-                            {
-                                resultVN = ZeroVN;
-                            }
-                            else if (arg1VN == ZeroVN)
-                            {
-                                resultVN = ZeroVN;
-                            }
-                        }
-                        break;
-
-                    case GT_DIV:
-                    case GT_UDIV:
-                        // (x / 1) => x
-                        OneVN = VNOneForType(typ);
-                        if (OneVN != NoVN)
-                        {
-                            if (arg1VN == OneVN)
-                            {
-                                resultVN = arg0VN;
-                            }
-                        }
-                        break;
+            // In terms of values, a castclass always returns its second argument, the object being cast.
+            // The operation may also throw an exception
+            ValueNum vnExcSet = VNExcSetSingleton(VNForFunc(TYP_REF, VNF_InvalidCastExc, arg1VN, arg0VN));
+            resultVN          = VNWithExc(arg1VN, vnExcSet);
+        }
+        else
+        {
+            resultVN = EvalUsingMathIdentity(typ, func, arg0VN, arg1VN);
 
-                    case GT_OR:
-                    case GT_XOR:
-                        // (x | 0) == (0 | x) => x
-                        // (x ^ 0) == (0 ^ x) => x
-                        ZeroVN = VNZeroForType(typ);
-                        if (arg0VN == ZeroVN)
-                        {
-                            resultVN = arg1VN;
-                        }
-                        else if (arg1VN == ZeroVN)
-                        {
-                            resultVN = arg0VN;
-                        }
-                        break;
+            // Do we have a valid resultVN?
+            if ((resultVN == NoVN) || (TypeOfVN(resultVN) != typ))
+            {
+                // Otherwise, Allocate a new ValueNum for 'func'('arg0VN','arg1VN')
+                //
+                Chunk*   c                                                     = GetAllocChunk(typ, CEA_Func2);
+                unsigned offsetWithinChunk                                     = c->AllocVN();
+                resultVN                                                       = c->m_baseVN + offsetWithinChunk;
+                reinterpret_cast<VNDefFunc2Arg*>(c->m_defs)[offsetWithinChunk] = fstruct;
+                // Record 'resultVN' in the Func2Map
+                GetVNFunc2Map()->Set(fstruct, resultVN);
+            }
+        }
+    }
+    return resultVN;
+}
 
-                    case GT_AND:
-                        // (x & 0) == (0 & x) => 0
-                        ZeroVN = VNZeroForType(typ);
-                        if (arg0VN == ZeroVN)
-                        {
-                            resultVN = ZeroVN;
-                        }
-                        else if (arg1VN == ZeroVN)
-                        {
-                            resultVN = ZeroVN;
-                        }
-                        break;
+//----------------------------------------------------------------------------------------
+//  VNForFunc  - Returns the ValueNum associated with 'func'('arg0VN','arg1VN','arg2VN')
+//               There is a one-to-one relationship between the ValueNum
+//               and 'func'('arg0VN','arg1VN','arg2VN')
+//
+// Arguments:
+//    typ            - The type of the resulting ValueNum produced by 'func'
+//    func           - Any binary VNFunc
+//    arg0VN         - The ValueNum of the first argument to 'func'
+//    arg1VN         - The ValueNum of the second argument to 'func'
+//    arg2VN         - The ValueNum of the third argument to 'func'
+//
+// Return Value:     - Returns the ValueNum associated with 'func'('arg0VN','arg1VN','arg1VN)
+//
+// Note: - This method only handles Trinary operations
+//         We have to special case VNF_PhiDef, as it's first two arguments are not ValueNums
+//
+ValueNum ValueNumStore::VNForFunc(var_types typ, VNFunc func, ValueNum arg0VN, ValueNum arg1VN, ValueNum arg2VN)
+{
+    assert(arg0VN != NoVN);
+    assert(arg1VN != NoVN);
+    assert(arg2VN != NoVN);
+    assert(VNFuncArity(func) == 3);
 
-                    case GT_LSH:
-                    case GT_RSH:
-                    case GT_RSZ:
-                    case GT_ROL:
-                    case GT_ROR:
-                        // (x << 0) => x
-                        // (x >> 0) => x
-                        // (x rol 0) => x
-                        // (x ror 0) => x
-                        ZeroVN = VNZeroForType(typ);
-                        if (arg1VN == ZeroVN)
-                        {
-                            resultVN = arg0VN;
-                        }
-                        break;
+#ifdef DEBUG
+    // Function arguments carry no exceptions.
+    //
+    if (func != VNF_PhiDef)
+    {
+        // For a phi definition first and second argument are "plain" local/ssa numbers.
+        // (I don't know if having such non-VN arguments to a VN function is a good idea -- if we wanted to declare
+        // ValueNum to be "short" it would be a problem, for example.  But we'll leave it for now, with these explicit
+        // exceptions.)
+        assert(arg0VN == VNNormalValue(arg0VN));
+        assert(arg1VN == VNNormalValue(arg1VN));
+    }
+    assert(arg2VN == VNNormalValue(arg2VN));
+#endif
+    assert(VNFuncArity(func) == 3);
 
-                    case GT_EQ:
-                    case GT_GE:
-                    case GT_LE:
-                        // (x == x) => true (unless x is NaN)
-                        // (x <= x) => true (unless x is NaN)
-                        // (x >= x) => true (unless x is NaN)
-                        if (VNIsEqual(arg0VN, arg1VN))
-                        {
-                            resultVN = VNOneForType(typ);
-                        }
-                        if ((arg0VN == VNForNull() && IsKnownNonNull(arg1VN)) ||
-                            (arg1VN == VNForNull() && IsKnownNonNull(arg0VN)))
-                        {
-                            resultVN = VNZeroForType(typ);
-                        }
-                        break;
+    ValueNum resultVN;
 
-                    case GT_NE:
-                    case GT_GT:
-                    case GT_LT:
-                        // (x != x) => false (unless x is NaN)
-                        // (x > x) => false (unless x is NaN)
-                        // (x < x) => false (unless x is NaN)
-                        if (VNIsEqual(arg0VN, arg1VN))
-                        {
-                            resultVN = VNZeroForType(typ);
-                        }
-                        if ((arg0VN == VNForNull() && IsKnownNonNull(arg1VN)) ||
-                            (arg1VN == VNForNull() && IsKnownNonNull(arg0VN)))
-                        {
-                            resultVN = VNOneForType(typ);
-                        }
-                        break;
+    // Have we already assigned a ValueNum for 'func'('arg0VN','arg1VN','arg2VN') ?
+    //
+    VNDefFunc3Arg fstruct(func, arg0VN, arg1VN, arg2VN);
+    if (!GetVNFunc3Map()->Lookup(fstruct, &resultVN))
+    {
+        // Otherwise, Allocate a new ValueNum for 'func'('arg0VN','arg1VN','arg2VN')
+        //
+        Chunk*   c                                                     = GetAllocChunk(typ, CEA_Func3);
+        unsigned offsetWithinChunk                                     = c->AllocVN();
+        resultVN                                                       = c->m_baseVN + offsetWithinChunk;
+        reinterpret_cast<VNDefFunc3Arg*>(c->m_defs)[offsetWithinChunk] = fstruct;
+        // Record 'resultVN' in the Func3Map
+        GetVNFunc3Map()->Set(fstruct, resultVN);
+    }
+    return resultVN;
+}
 
-                    default:
-                        break;
-                }
+// ----------------------------------------------------------------------------------------
+//  VNForFunc  - Returns the ValueNum associated with 'func'('arg0VN','arg1VN','arg2VN','arg3VN')
+//               There is a one-to-one relationship between the ValueNum
+//               and 'func'('arg0VN','arg1VN','arg2VN','arg3VN')
+//
+// Arguments:
+//    typ            - The type of the resulting ValueNum produced by 'func'
+//    func           - Any binary VNFunc
+//    arg0VN         - The ValueNum of the first argument to 'func'
+//    arg1VN         - The ValueNum of the second argument to 'func'
+//    arg2VN         - The ValueNum of the third argument to 'func'
+//    arg3VN         - The ValueNum of the fourth argument to 'func'
+//
+// Return Value:     - Returns the ValueNum associated with 'func'('arg0VN','arg1VN','arg2VN','arg3VN')
+//
+// Note:   Currently the only four operand func is the VNF_PtrToArrElem operation
+//
+ValueNum ValueNumStore::VNForFunc(
+    var_types typ, VNFunc func, ValueNum arg0VN, ValueNum arg1VN, ValueNum arg2VN, ValueNum arg3VN)
+{
+    assert(arg0VN != NoVN && arg1VN != NoVN && arg2VN != NoVN && arg3VN != NoVN);
 
-                if ((resultVN != NoVN) && (TypeOfVN(resultVN) == typ))
-                {
-                    return resultVN;
-                }
-            }
-        }
-        else // must be a VNF_ function
-        {
-            if (VNIsEqual(arg0VN, arg1VN))
-            {
-                // x <= x ==> true
-                // x >= x ==> true
-                if ((func == VNF_LE_UN) || (func == VNF_GE_UN))
-                {
-                    return VNOneForType(typ);
-                }
-                // x < x ==> false
-                // x > x ==> false
-                else if ((func == VNF_LT_UN) || (func == VNF_GT_UN))
-                {
-                    return VNZeroForType(typ);
-                }
-            }
+    // Function arguments carry no exceptions.
+    assert(arg0VN == VNNormalValue(arg0VN));
+    assert(arg1VN == VNNormalValue(arg1VN));
+    assert(arg2VN == VNNormalValue(arg2VN));
+    assert(arg3VN == VNNormalValue(arg3VN));
+    assert(VNFuncArity(func) == 4);
 
-            if (func == VNF_CastClass)
-            {
-                // In terms of values, a castclass always returns its second argument, the object being cast.
-                // The IL operation may also throw an exception
-                return VNWithExc(arg1VN, VNExcSetSingleton(VNForFunc(TYP_REF, VNF_InvalidCastExc, arg1VN, arg0VN)));
-            }
-        }
+    ValueNum resultVN;
 
-        // Otherwise, assign a new VN for the function application.
-        Chunk*   c                                                     = GetAllocChunk(typ, CEA_Func2);
+    // Have we already assigned a ValueNum for 'func'('arg0VN','arg1VN','arg2VN','arg3VN') ?
+    //
+    VNDefFunc4Arg fstruct(func, arg0VN, arg1VN, arg2VN, arg3VN);
+    if (!GetVNFunc4Map()->Lookup(fstruct, &resultVN))
+    {
+        // Otherwise, Allocate a new ValueNum for 'func'('arg0VN','arg1VN','arg2VN','arg3VN')
+        //
+        Chunk*   c                                                     = GetAllocChunk(typ, CEA_Func4);
         unsigned offsetWithinChunk                                     = c->AllocVN();
-        res                                                            = c->m_baseVN + offsetWithinChunk;
-        reinterpret_cast<VNDefFunc2Arg*>(c->m_defs)[offsetWithinChunk] = fstruct;
-        GetVNFunc2Map()->Set(fstruct, res);
-        return res;
+        resultVN                                                       = c->m_baseVN + offsetWithinChunk;
+        reinterpret_cast<VNDefFunc4Arg*>(c->m_defs)[offsetWithinChunk] = fstruct;
+        // Record 'resultVN' in the Func4Map
+        GetVNFunc4Map()->Set(fstruct, resultVN);
     }
+    return resultVN;
 }
 
 //------------------------------------------------------------------------------
@@ -3190,6 +3087,7 @@ bool ValueNumStore::CanEvalForConstantArgs(VNFunc vnf)
         {
             case VNF_Cast: // We can evaluate these.
                 return true;
+
             case VNF_ObjGetType:
                 return false;
             default:
@@ -3198,74 +3096,346 @@ bool ValueNumStore::CanEvalForConstantArgs(VNFunc vnf)
     }
 }
 
-ValueNum ValueNumStore::VNForFunc(var_types typ, VNFunc func, ValueNum arg0VN, ValueNum arg1VN, ValueNum arg2VN)
+//----------------------------------------------------------------------------------------
+//  VNEvalShouldFold - Returns true if we should perform the folding operation.
+//                     It returns false if we don't want to fold the expression,
+//                     because it will always throw an exception.
+//
+// Arguments:
+//    typ            - The type of the resulting ValueNum produced by 'func'
+//    func           - Any binary VNFunc
+//    arg0VN         - The ValueNum of the first argument to 'func'
+//    arg1VN         - The ValueNum of the second argument to 'func'
+//
+// Return Value:     - Returns true if we should perform a folding operation.
+//
+bool ValueNumStore::VNEvalShouldFold(var_types typ, VNFunc func, ValueNum arg0VN, ValueNum arg1VN)
 {
-    assert(arg0VN != NoVN);
-    assert(arg1VN != NoVN);
-    assert(arg2VN != NoVN);
-    assert(VNFuncArity(func) == 3);
-
-    // Function arguments carry no exceptions.
-    CLANG_FORMAT_COMMENT_ANCHOR;
+    bool shouldFold = true;
 
-#ifdef DEBUG
-    if (func != VNF_PhiDef)
+    // We have some arithmetic operations that will always throw
+    // an exception given particular constant argument(s).
+    // (i.e. integer division by zero)
+    //
+    // We will avoid performing any constant folding on them
+    // since they won't actually produce any result.
+    // Instead they always will throw an exception.
+    //
+    if (func < VNF_Boundary)
     {
-        // For a phi definition first and second argument are "plain" local/ssa numbers.
-        // (I don't know if having such non-VN arguments to a VN function is a good idea -- if we wanted to declare
-        // ValueNum to be "short" it would be a problem, for example.  But we'll leave it for now, with these explicit
-        // exceptions.)
-        assert(arg0VN == VNNormalValue(arg0VN));
-        assert(arg1VN == VNNormalValue(arg1VN));
-    }
-    assert(arg2VN == VNNormalValue(arg2VN));
+        genTreeOps oper = genTreeOps(func);
 
-#endif
-    assert(VNFuncArity(func) == 3);
+        // Floating point operations do not throw exceptions
+        //
+        if (!varTypeIsFloating(typ))
+        {
+            // Is this an integer divide/modulo that will always throw an exception?
+            //
+            if ((oper == GT_DIV) || (oper == GT_UDIV) || (oper == GT_MOD) || (oper == GT_UMOD))
+            {
+                if ((TypeOfVN(arg0VN) != typ) || (TypeOfVN(arg1VN) != typ))
+                {
+                    // Just in case we have mismatched types
+                    shouldFold = false;
+                }
+                else
+                {
+                    bool isUnsigned = (oper == GT_UDIV) || (oper == GT_UMOD);
+                    if (typ == TYP_LONG)
+                    {
+                        INT64 kArg0 = ConstantValue<INT64>(arg0VN);
+                        INT64 kArg1 = ConstantValue<INT64>(arg1VN);
 
-    ValueNum      res;
-    VNDefFunc3Arg fstruct(func, arg0VN, arg1VN, arg2VN);
-    if (GetVNFunc3Map()->Lookup(fstruct, &res))
-    {
-        return res;
+                        if (IsIntZero(kArg1))
+                        {
+                            // Don't fold, we have a divide by zero
+                            shouldFold = false;
+                        }
+                        else if (!isUnsigned || IsOverflowIntDiv(kArg0, kArg1))
+                        {
+                            // Don't fold, we have a divide of INT64_MIN/-1
+                            shouldFold = false;
+                        }
+                    }
+                    else if (typ == TYP_INT)
+                    {
+                        int kArg0 = ConstantValue<int>(arg0VN);
+                        int kArg1 = ConstantValue<int>(arg1VN);
+
+                        if (IsIntZero(kArg1))
+                        {
+                            // Don't fold, we have a divide by zero
+                            shouldFold = false;
+                        }
+                        else if (!isUnsigned && IsOverflowIntDiv(kArg0, kArg1))
+                        {
+                            // Don't fold, we have a divide of INT32_MIN/-1
+                            shouldFold = false;
+                        }
+                    }
+                    else // strange value for 'typ'
+                    {
+                        assert(!"unexpected 'typ' in VNForFunc constant folding");
+                        shouldFold = false;
+                    }
+                }
+            }
+        }
     }
-    else
+    else // (func > VNF_Boundary)
     {
-        Chunk*   c                                                     = GetAllocChunk(typ, CEA_Func3);
-        unsigned offsetWithinChunk                                     = c->AllocVN();
-        res                                                            = c->m_baseVN + offsetWithinChunk;
-        reinterpret_cast<VNDefFunc3Arg*>(c->m_defs)[offsetWithinChunk] = fstruct;
-        GetVNFunc3Map()->Set(fstruct, res);
-        return res;
+        // OK to fold,
+        // Add checks in the future if we support folding of VNF_ADD_OVF, etc...
     }
+
+    return shouldFold;
 }
 
-ValueNum ValueNumStore::VNForFunc(
-    var_types typ, VNFunc func, ValueNum arg0VN, ValueNum arg1VN, ValueNum arg2VN, ValueNum arg3VN)
+//----------------------------------------------------------------------------------------
+//  EvalUsingMathIdentity
+//                   - Attempts to evaluate 'func' by using mathimatical identities
+//                     that can be appied to 'func'.
+//
+// Arguments:
+//    typ            - The type of the resulting ValueNum produced by 'func'
+//    func           - Any binary VNFunc
+//    arg0VN         - The ValueNum of the first argument to 'func'
+//    arg1VN         - The ValueNum of the second argument to 'func'
+//
+// Return Value:     - When successful a  ValueNum for the expression is returned.
+//                     When unsuccessful NoVN is returned.
+//
+ValueNum ValueNumStore::EvalUsingMathIdentity(var_types typ, VNFunc func, ValueNum arg0VN, ValueNum arg1VN)
 {
-    assert(arg0VN != NoVN && arg1VN != NoVN && arg2VN != NoVN && arg3VN != NoVN);
-    // Function arguments carry no exceptions.
-    assert(arg0VN == VNNormalValue(arg0VN));
-    assert(arg1VN == VNNormalValue(arg1VN));
-    assert(arg2VN == VNNormalValue(arg2VN));
-    assert(arg3VN == VNNormalValue(arg3VN));
-    assert(VNFuncArity(func) == 4);
+    ValueNum resultVN = NoVN; // set default result to unsuccessful
+
+    if (typ == TYP_BYREF) // We don't want/need to optimize a zero byref
+    {
+        return resultVN; // return the unsuccessful value
+    }
+
+    // We have ways of evaluating some binary functions.
+    if (func < VNF_Boundary)
+    {
+        switch (genTreeOps(func))
+        {
+            ValueNum ZeroVN;
+            ValueNum OneVN;
+
+            case GT_ADD:
+                // (0 + x) == x
+                // (x + 0) == x
+                // This identity does not apply for floating point (when x == -0.0)
+                //
+                if (!varTypeIsFloating(typ))
+                {
+                    ZeroVN = VNZeroForType(typ);
+                    if (VNIsEqual(arg0VN, ZeroVN))
+                    {
+                        resultVN = arg1VN;
+                    }
+                    else if (VNIsEqual(arg1VN, ZeroVN))
+                    {
+                        resultVN = arg0VN;
+                    }
+                }
+                break;
+
+            case GT_SUB:
+                // (x - 0) == x
+                // (x - x) == 0
+                // This identity does not apply for floating point (when x == -0.0)
+                //
+                if (!varTypeIsFloating(typ))
+                {
+                    ZeroVN = VNZeroForType(typ);
+                    if (VNIsEqual(arg1VN, ZeroVN))
+                    {
+                        resultVN = arg0VN;
+                    }
+                    else if (VNIsEqual(arg0VN, arg1VN))
+                    {
+                        resultVN = ZeroVN;
+                    }
+                }
+                break;
+
+            case GT_MUL:
+                // These identities do not apply for floating point
+                //
+                if (!varTypeIsFloating(typ))
+                {
+                    // (0 * x) == 0
+                    // (x * 0) == 0
+                    ZeroVN = VNZeroForType(typ);
+                    if (arg0VN == ZeroVN)
+                    {
+                        resultVN = ZeroVN;
+                    }
+                    else if (arg1VN == ZeroVN)
+                    {
+                        resultVN = ZeroVN;
+                    }
+
+                    // (x * 1) == x
+                    // (1 * x) == x
+                    OneVN = VNOneForType(typ);
+                    if (arg0VN == OneVN)
+                    {
+                        resultVN = arg1VN;
+                    }
+                    else if (arg1VN == OneVN)
+                    {
+                        resultVN = arg0VN;
+                    }
+                }
+                break;
+
+            case GT_DIV:
+            case GT_UDIV:
+                // (x / 1) == x
+                // This identity does not apply for floating point
+                //
+                if (!varTypeIsFloating(typ))
+                {
+                    OneVN = VNOneForType(typ);
+                    if (arg1VN == OneVN)
+                    {
+                        resultVN = arg0VN;
+                    }
+                }
+                break;
+
+            case GT_OR:
+            case GT_XOR:
+                // (0 | x) == x,  (0 ^ x) == x
+                // (x | 0) == x,  (x ^ 0) == x
+                ZeroVN = VNZeroForType(typ);
+                if (arg0VN == ZeroVN)
+                {
+                    resultVN = arg1VN;
+                }
+                else if (arg1VN == ZeroVN)
+                {
+                    resultVN = arg0VN;
+                }
+                break;
+
+            case GT_AND:
+                // (x & 0) == 0
+                // (0 & x) == 0
+                ZeroVN = VNZeroForType(typ);
+                if (arg0VN == ZeroVN)
+                {
+                    resultVN = ZeroVN;
+                }
+                else if (arg1VN == ZeroVN)
+                {
+                    resultVN = ZeroVN;
+                }
+                break;
+
+            case GT_LSH:
+            case GT_RSH:
+            case GT_RSZ:
+            case GT_ROL:
+            case GT_ROR:
+                // (x << 0)  == x
+                // (x >> 0)  == x
+                // (x rol 0) == x
+                // (x ror 0) == x
+                ZeroVN = VNZeroForType(typ);
+                if (arg1VN == ZeroVN)
+                {
+                    resultVN = arg0VN;
+                }
+                // (0 << x)  == 0
+                // (0 >> x)  == 0
+                // (0 rol x) == 0
+                // (0 ror x) == 0
+                if (arg0VN == ZeroVN)
+                {
+                    resultVN = ZeroVN;
+                }
+                break;
+
+            case GT_EQ:
+            case GT_GE:
+            case GT_LE:
+                // (x == x) == true,  (null == non-null) == false,  (non-null == null) == false
+                // (x <= x) == true,  (null <= non-null) == false,  (non-null <= null) == false
+                // (x >= x) == true,  (null >= non-null) == false,  (non-null >= null) == false
+                //
+                // This identity does not apply for floating point (when x == NaN)
+                //
+                if (!varTypeIsFloating(typ))
+                {
+                    if (VNIsEqual(arg0VN, arg1VN))
+                    {
+                        resultVN = VNOneForType(typ);
+                    }
+                    if ((arg0VN == VNForNull()) && IsKnownNonNull(arg1VN))
+                    {
+                        resultVN = VNZeroForType(typ);
+                    }
+                    if (IsKnownNonNull(arg0VN) && (arg1VN == VNForNull()))
+                    {
+                        resultVN = VNZeroForType(typ);
+                    }
+                }
+                break;
+
+            case GT_NE:
+            case GT_GT:
+            case GT_LT:
+                // (x != x) == false,  (null != non-null) == true,  (non-null != null) == true
+                // (x > x)  == false,  (null == non-null) == true,  (non-null == null) == true
+                // (x < x)  == false,  (null == non-null) == true,  (non-null == null) == true
+                //
+                // This identity does not apply for floating point (when x == NaN)
+                //
+                if (!varTypeIsFloating(typ))
+                {
+                    if (VNIsEqual(arg0VN, arg1VN))
+                    {
+                        resultVN = VNZeroForType(typ);
+                    }
+                    if ((arg0VN == VNForNull()) && IsKnownNonNull(arg1VN))
+                    {
+                        resultVN = VNOneForType(typ);
+                    }
+                    if (IsKnownNonNull(arg0VN) && (arg1VN == VNForNull()))
+                    {
+                        resultVN = VNOneForType(typ);
+                    }
+                }
+                break;
 
-    ValueNum      res;
-    VNDefFunc4Arg fstruct(func, arg0VN, arg1VN, arg2VN, arg3VN);
-    if (GetVNFunc4Map()->Lookup(fstruct, &res))
-    {
-        return res;
+            default:
+                break;
+        }
     }
-    else
+    else // must be a VNF_ function
     {
-        Chunk*   c                                                     = GetAllocChunk(typ, CEA_Func4);
-        unsigned offsetWithinChunk                                     = c->AllocVN();
-        res                                                            = c->m_baseVN + offsetWithinChunk;
-        reinterpret_cast<VNDefFunc4Arg*>(c->m_defs)[offsetWithinChunk] = fstruct;
-        GetVNFunc4Map()->Set(fstruct, res);
-        return res;
+        // These identities do not apply for floating point (when x == NaN)
+        //
+        if (VNIsEqual(arg0VN, arg1VN))
+        {
+            // x <= x == true
+            // x >= x == true
+            if ((func == VNF_LE_UN) || (func == VNF_GE_UN))
+            {
+                resultVN = VNOneForType(typ);
+            }
+            // x < x == false
+            // x > x == false
+            else if ((func == VNF_LT_UN) || (func == VNF_GT_UN))
+            {
+                resultVN = VNZeroForType(typ);
+            }
+        }
     }
+    return resultVN;
 }
 
 //------------------------------------------------------------------------
@@ -6827,6 +6997,19 @@ void Compiler::fgValueNumberTree(GenTree* tree)
             // Now that we've labeled the assignment as a whole, we don't care about exceptions.
             rhsVNPair = vnStore->VNPNormalPair(rhsVNPair);
 
+            // Record the exeception set for this 'tree' in vnExcSet.
+            // First we'll record the exeception set for the rhs and
+            // later we will union in the exeception set for the lhs
+            //
+            ValueNum vnExcSet = ValueNumStore::VNForEmptyExcSet();
+
+            // Unpack, Norm,Exc for 'rhsVNPair'
+            ValueNum vnRhsLibNorm;
+            vnStore->VNUnpackExc(rhsVNPair.GetLiberal(), &vnRhsLibNorm, &vnExcSet);
+
+            // Now that we've saved the rhs exeception set, we we will use the normal values.
+            rhsVNPair = ValueNumPair(vnRhsLibNorm, vnStore->VNNormalValue(rhsVNPair.GetConservative()));
+
             // If the types of the rhs and lhs are different then we
             //  may want to change the ValueNumber assigned to the lhs.
             //
@@ -7215,8 +7398,8 @@ void Compiler::fgValueNumberTree(GenTree* tree)
                                 // otherwise it is the type returned from VNApplySelectors above.
                                 var_types firstFieldType = vnStore->TypeOfVN(fldMapVN);
 
-                                ValueNum storeVal =
-                                    rhsVNPair.GetLiberal(); // The value number from the rhs of the assignment
+                                // The value number from the rhs of the assignment
+                                ValueNum storeVal    = rhsVNPair.GetLiberal();
                                 ValueNum newFldMapVN = ValueNumStore::NoVN;
 
                                 // when (obj != nullptr) we have an instance field, otherwise a static field
@@ -7229,8 +7412,12 @@ void Compiler::fgValueNumberTree(GenTree* tree)
 
                                     if (obj != nullptr)
                                     {
+                                        // Unpack, Norm,Exc for 'obj'
+                                        ValueNum vnObjExcSet = ValueNumStore::VNForEmptyExcSet();
+                                        vnStore->VNUnpackExc(obj->gtVNPair.GetLiberal(), &normVal, &vnObjExcSet);
+                                        vnExcSet = vnStore->VNExcSetUnion(vnExcSet, vnObjExcSet);
+
                                         // construct the ValueNumber for 'fldMap at obj'
-                                        normVal = vnStore->VNLiberalNormalValue(obj->gtVNPair);
                                         valAtAddr =
                                             vnStore->VNForMapSelect(VNK_Liberal, firstFieldType, fldMapVN, normVal);
                                     }
@@ -7728,7 +7915,7 @@ void Compiler::fgValueNumberTree(GenTree* tree)
                         }
                     }
                 }
-                else
+                else // we have a binary oper
                 {
                     assert(oper != GT_ASG); // We handled assignments earlier.
                     assert(GenTree::OperIsBinary(oper));
@@ -7736,49 +7923,49 @@ void Compiler::fgValueNumberTree(GenTree* tree)
                     ValueNumPair op2VNPair;
                     if (tree->gtOp.gtOp2 == nullptr)
                     {
+                        // Handle any GT_LIST nodes as they can have a nullptr for op2.
                         op2VNPair.SetBoth(ValueNumStore::VNForNull());
                     }
                     else
                     {
                         op2VNPair = tree->gtOp.gtOp2->gtVNPair;
                     }
-                    // A few special case: if we add a field offset constant to a PtrToXXX, we get back a new PtrToXXX.
-                    ValueNum newVN = ValueNumStore::NoVN;
+
+                    // Handle a few special cases: if we add a field offset constant to a PtrToXXX, we will get back a
+                    // new
+                    // PtrToXXX.
 
                     ValueNumPair op1vnp;
                     ValueNumPair op1Xvnp = ValueNumStore::VNPForEmptyExcSet();
                     vnStore->VNPUnpackExc(tree->gtOp.gtOp1->gtVNPair, &op1vnp, &op1Xvnp);
+
                     ValueNumPair op2vnp;
                     ValueNumPair op2Xvnp = ValueNumStore::VNPForEmptyExcSet();
                     vnStore->VNPUnpackExc(op2VNPair, &op2vnp, &op2Xvnp);
                     ValueNumPair excSet = vnStore->VNPExcSetUnion(op1Xvnp, op2Xvnp);
 
-                    if (oper == GT_ADD)
+                    ValueNum newVN = ValueNumStore::NoVN;
+
+                    // Check for the addition of a field offset constant
+                    //
+                    if ((oper == GT_ADD) && (!tree->gtOverflowEx()))
                     {
                         newVN = vnStore->ExtendPtrVN(tree->gtOp.gtOp1, tree->gtOp.gtOp2);
-                        if (newVN == ValueNumStore::NoVN)
-                        {
-                            newVN = vnStore->ExtendPtrVN(tree->gtOp.gtOp2, tree->gtOp.gtOp1);
-                        }
                     }
+
                     if (newVN != ValueNumStore::NoVN)
                     {
-                        newVN = vnStore->VNWithExc(newVN, excSet.GetLiberal());
                         // We don't care about differences between liberal and conservative for pointer values.
+                        newVN = vnStore->VNWithExc(newVN, excSet.GetLiberal());
                         tree->gtVNPair.SetBoth(newVN);
                     }
                     else
                     {
-
-                        ValueNumPair normalRes = vnStore->VNPairForFunc(tree->TypeGet(), vnf, op1vnp, op2vnp);
-                        // Overflow-checking operations add an overflow exception
-                        if (tree->gtOverflowEx())
-                        {
-                            ValueNum overflowExcSet = vnStore->VNExcSetSingleton(
-                                vnStore->VNForFunc(TYP_REF, VNF_OverflowExc, vnStore->VNForVoid()));
-                            excSet = vnStore->VNPExcSetUnion(excSet, ValueNumPair(overflowExcSet, overflowExcSet));
-                        }
-                        tree->gtVNPair = vnStore->VNPWithExc(normalRes, excSet);
+                        VNFunc       vnf        = GetVNFuncForNode(tree);
+                        ValueNumPair normalPair = vnStore->VNPairForFunc(tree->TypeGet(), vnf, op1vnp, op2vnp);
+                        tree->gtVNPair          = vnStore->VNPWithExc(normalPair, excSet);
+                        // For overflow checking operations the VNF_OverflowExc will be added below
+                        // by fgValueNumberAddExceptionSet
                     }
                 }
             }
@@ -7816,14 +8003,13 @@ void Compiler::fgValueNumberTree(GenTree* tree)
 
                     case GT_NULLCHECK:
                     {
-                        // Explicit null check.
-                        // Handle case where operand tree also may cause exceptions.
-                        ValueNumPair excSet = vnStore->VNPExcSetSingleton(
-                            vnStore->VNPairForFunc(TYP_REF, VNF_NullPtrExc,
-                                                   vnStore->VNPNormalPair(tree->gtOp.gtOp1->gtVNPair)));
-                        ValueNumPair excSetBoth =
-                            vnStore->VNPExcSetUnion(excSet, vnStore->VNPExceptionSet(tree->gtOp.gtOp1->gtVNPair));
-                        tree->gtVNPair = vnStore->VNPWithExc(vnStore->VNPForVoid(), excSetBoth);
+                        // An Explicit null check, produces no value
+                        // But we do persist any execeptions produced by op1
+                        //
+                        tree->gtVNPair = vnStore->VNPWithExc(vnStore->VNPForVoid(),
+                                                             vnStore->VNPExceptionSet(tree->gtOp.gtOp1->gtVNPair));
+                        // The exception set with VNF_NullPtrExc will be added below
+                        // by fgValueNumberAddExceptionSet
                     }
                     break;
 
@@ -7856,6 +8042,9 @@ void Compiler::fgValueNumberTree(GenTree* tree)
                 }
             }
         }
+
+        // next we add any exception sets for the current tree node
+        fgValueNumberAddExceptionSet(tree);
     }
     else
     {
@@ -8369,7 +8558,7 @@ void Compiler::fgUpdateArgListVNs(GenTreeArgList* args)
     fgValueNumberTree(args);
 }
 
-VNFunc Compiler::fgValueNumberHelperMethVNFunc(CorInfoHelpFunc helpFunc)
+VNFunc Compiler::fgValueNumberJitHelperMethodVNFunc(CorInfoHelpFunc helpFunc)
 {
     assert(s_helperCallProperties.IsPure(helpFunc) || s_helperCallProperties.IsAllocator(helpFunc));
 
@@ -8694,7 +8883,7 @@ bool Compiler::fgValueNumberHelperCall(GenTreeCall* call)
 
         if (!needsFurtherWork && (pure || isAlloc))
         {
-            VNFunc vnf = fgValueNumberHelperMethVNFunc(helpFunc);
+            VNFunc vnf = fgValueNumberJitHelperMethodVNFunc(helpFunc);
 
             if (mayRunCctor)
             {
@@ -8717,6 +8906,465 @@ bool Compiler::fgValueNumberHelperCall(GenTreeCall* call)
     return modHeap;
 }
 
+//--------------------------------------------------------------------------------
+// fgValueNumberAddExceptionSetForIndirection
+//         - Adds the exception sets for the current tree node
+//           which is performing a memory indirection operation
+//
+// Arguments:
+//    tree       - The current GenTree node,
+//                 It must be some kind of an indirection node
+//
+// Return Value:
+//               - The tree's gtVNPair is updated to include the VNF_nullPtrExc
+//                 exception set.  We calculate a base address to use as the
+//                 argument to the VNF_nullPtrExc function.
+//
+// Notes:        - The calculation of the base address removes any constant
+//                 offsets, so that obj.x and obj.y will both have obj as
+//                 their base address.
+//                 For arrays the base address currently includes the
+//                 index calculations.
+//
+void Compiler::fgValueNumberAddExceptionSetForIndirection(GenTree* tree)
+{
+    // We should have an Unary operator
+    assert(tree->OperIsUnary());
+    GenTree* baseAddr = tree->gtGetOp1();
+
+    // We evaluate the baseAddr ValueNumber further in order
+    // to obtain a better value to use for the null check exeception.
+    //
+    ValueNumPair baseVNP = baseAddr->gtVNPair;
+    ValueNum     baseLVN = baseVNP.GetLiberal();
+    ValueNum     baseCVN = baseVNP.GetConservative();
+    ssize_t      offsetL = 0;
+    ssize_t      offsetC = 0;
+    VNFuncApp    funcAttr;
+
+    while (vnStore->GetVNFunc(baseLVN, &funcAttr) && (funcAttr.m_func == (VNFunc)GT_ADD) &&
+           (vnStore->TypeOfVN(baseLVN) == TYP_BYREF))
+    {
+        if (fgIsBigOffset(offsetL))
+        {
+            // Failure: Exit this loop if we have a "big" offset
+
+            // reset baseLVN back to the full address expression
+            baseLVN = baseVNP.GetLiberal();
+            break;
+        }
+
+        // The arguments in value numbering functions are sorted in increasing order
+        // Thus either arg could be the constant.
+        if (vnStore->IsVNConstant(funcAttr.m_args[0]) && varTypeIsIntegral(vnStore->TypeOfVN(funcAttr.m_args[0])))
+        {
+            offsetL += vnStore->CoercedConstantValue<ssize_t>(funcAttr.m_args[0]);
+            baseLVN = funcAttr.m_args[1];
+        }
+        else if (vnStore->IsVNConstant(funcAttr.m_args[1]) && varTypeIsIntegral(vnStore->TypeOfVN(funcAttr.m_args[1])))
+        {
+            offsetL += vnStore->CoercedConstantValue<ssize_t>(funcAttr.m_args[1]);
+            baseLVN = funcAttr.m_args[0];
+        }
+        else // neither argument is a constant
+        {
+            break;
+        }
+    }
+
+    while (vnStore->GetVNFunc(baseCVN, &funcAttr) && (funcAttr.m_func == (VNFunc)GT_ADD) &&
+           (vnStore->TypeOfVN(baseCVN) == TYP_BYREF))
+    {
+        if (fgIsBigOffset(offsetC))
+        {
+            // Failure: Exit this loop if we have a "big" offset
+
+            // reset baseCVN back to the full address expression
+            baseCVN = baseVNP.GetConservative();
+            break;
+        }
+
+        // The arguments in value numbering functions are sorted in increasing order
+        // Thus either arg could be the constant.
+        if (vnStore->IsVNConstant(funcAttr.m_args[0]) && varTypeIsIntegral(vnStore->TypeOfVN(funcAttr.m_args[0])))
+        {
+            offsetL += vnStore->CoercedConstantValue<ssize_t>(funcAttr.m_args[0]);
+            baseCVN = funcAttr.m_args[1];
+        }
+        else if (vnStore->IsVNConstant(funcAttr.m_args[1]) && varTypeIsIntegral(vnStore->TypeOfVN(funcAttr.m_args[1])))
+        {
+            offsetC += vnStore->CoercedConstantValue<ssize_t>(funcAttr.m_args[1]);
+            baseCVN = funcAttr.m_args[0];
+        }
+        else // neither argument is a constant
+        {
+            break;
+        }
+    }
+
+    // Create baseVNP, from the values we just computed,
+    baseVNP = ValueNumPair(baseLVN, baseCVN);
+
+    // Unpack, Norm,Exc for the tree's op1 VN
+    ValueNumPair vnpBaseNorm;
+    ValueNumPair vnpBaseExc = ValueNumStore::VNPForEmptyExcSet();
+    vnStore->VNPUnpackExc(baseVNP, &vnpBaseNorm, &vnpBaseExc);
+
+    // The Norm VN for op1 is used to create the NullPtrExc
+    ValueNumPair excChkSet = vnStore->VNPExcSetSingleton(vnStore->VNPairForFunc(TYP_REF, VNF_NullPtrExc, vnpBaseNorm));
+
+    // Combine the excChkSet with exception set of op1
+    ValueNumPair excSetBoth = vnStore->VNPExcSetUnion(excChkSet, vnpBaseExc);
+
+    // Retrieve the Normal VN for tree, note that it may be NoVN, so we handle that case
+    ValueNumPair vnpNorm = vnStore->VNPNormalPair(tree->gtVNPair);
+
+    // For as GT_IND on the lhs of an assignment we will get a NoVN value
+    if (vnpNorm.GetLiberal() == ValueNumStore::NoVN)
+    {
+        // Use the special Void VN value instead.
+        vnpNorm = vnStore->VNPForVoid();
+    }
+    tree->gtVNPair = vnStore->VNPWithExc(vnpNorm, excSetBoth);
+}
+
+//--------------------------------------------------------------------------------
+// fgValueNumberAddExceptionSetForDivison
+//         - Adds the exception sets for the current tree node
+//           which is performing an integer division operation
+//
+// Arguments:
+//    tree       - The current GenTree node,
+//                 It must be a node that performs an integer division
+//
+// Return Value:
+//               - The tree's gtVNPair is updated to include
+//                 VNF_DivideByZeroExc and VNF_ArithmeticExc,
+//                 We will omit one or both of them when the operation
+//                 has constants argumemts that preclude the exception.
+//
+void Compiler::fgValueNumberAddExceptionSetForDivision(GenTree* tree)
+{
+    genTreeOps oper = tree->OperGet();
+
+    // A Divide By Zero exception may be possible.
+    // The divisor is held in tree->gtOp.gtOp2
+    //
+    bool isUnsignedOper         = (oper == GT_UDIV) || (oper == GT_UMOD);
+    bool needDivideByZeroExcLib = true;
+    bool needDivideByZeroExcCon = true;
+    bool needArithmeticExcLib   = !isUnsignedOper; // Overflow isn't possible for unsigned divide
+    bool needArithmeticExcCon   = !isUnsignedOper;
+
+    // Determine if we have a 32-bit or 64-bit divide operation
+    var_types typ = genActualType(tree->TypeGet());
+    assert((typ == TYP_INT) || (typ == TYP_LONG));
+
+    // Retrieve the Norm VN for op2 to use it for the DivideByZeroExc
+    ValueNumPair vnpOp2Norm   = vnStore->VNPNormalPair(tree->gtOp.gtOp2->gtVNPair);
+    ValueNum     vnOp2NormLib = vnpOp2Norm.GetLiberal();
+    ValueNum     vnOp2NormCon = vnpOp2Norm.GetConservative();
+
+    if (typ == TYP_INT)
+    {
+        if (vnStore->IsVNConstant(vnOp2NormLib))
+        {
+            INT32 kVal = vnStore->ConstantValue<INT32>(vnOp2NormLib);
+            if (kVal != 0)
+            {
+                needDivideByZeroExcLib = false;
+            }
+            if (!isUnsignedOper && (kVal != -1))
+            {
+                needArithmeticExcLib = false;
+            }
+        }
+        if (vnStore->IsVNConstant(vnOp2NormCon))
+        {
+            INT32 kVal = vnStore->ConstantValue<INT32>(vnOp2NormCon);
+            if (kVal != 0)
+            {
+                needDivideByZeroExcCon = false;
+            }
+            if (!isUnsignedOper && (kVal != -1))
+            {
+                needArithmeticExcCon = false;
+            }
+        }
+    }
+    else // (typ == TYP_LONG)
+    {
+        if (vnStore->IsVNConstant(vnOp2NormLib))
+        {
+            INT64 kVal = vnStore->ConstantValue<INT64>(vnOp2NormLib);
+            if (kVal != 0)
+            {
+                needDivideByZeroExcLib = false;
+            }
+            if (!isUnsignedOper && (kVal != -1))
+            {
+                needArithmeticExcLib = false;
+            }
+        }
+        if (vnStore->IsVNConstant(vnOp2NormCon))
+        {
+            INT64 kVal = vnStore->ConstantValue<INT64>(vnOp2NormCon);
+            if (kVal != 0)
+            {
+                needDivideByZeroExcCon = false;
+            }
+            if (!isUnsignedOper && (kVal != -1))
+            {
+                needArithmeticExcCon = false;
+            }
+        }
+    }
+
+    // Retrieve the Norm VN for op1 to use it for the ArithmeticExc
+    ValueNumPair vnpOp1Norm   = vnStore->VNPNormalPair(tree->gtOp.gtOp1->gtVNPair);
+    ValueNum     vnOp1NormLib = vnpOp1Norm.GetLiberal();
+    ValueNum     vnOp1NormCon = vnpOp1Norm.GetConservative();
+
+    if (needArithmeticExcLib || needArithmeticExcCon)
+    {
+        if (typ == TYP_INT)
+        {
+            if (vnStore->IsVNConstant(vnOp1NormLib))
+            {
+                INT32 kVal = vnStore->ConstantValue<INT32>(vnOp1NormLib);
+
+                if (!isUnsignedOper && (kVal != INT32_MIN))
+                {
+                    needArithmeticExcLib = false;
+                }
+            }
+            if (vnStore->IsVNConstant(vnOp1NormCon))
+            {
+                INT32 kVal = vnStore->ConstantValue<INT32>(vnOp1NormCon);
+
+                if (!isUnsignedOper && (kVal != INT32_MIN))
+                {
+                    needArithmeticExcCon = false;
+                }
+            }
+        }
+        else // (typ == TYP_LONG)
+        {
+            if (vnStore->IsVNConstant(vnOp1NormLib))
+            {
+                INT64 kVal = vnStore->ConstantValue<INT64>(vnOp1NormLib);
+
+                if (!isUnsignedOper && (kVal != INT64_MIN))
+                {
+                    needArithmeticExcLib = false;
+                }
+            }
+            if (vnStore->IsVNConstant(vnOp1NormCon))
+            {
+                INT64 kVal = vnStore->ConstantValue<INT64>(vnOp1NormCon);
+
+                if (!isUnsignedOper && (kVal != INT64_MIN))
+                {
+                    needArithmeticExcCon = false;
+                }
+            }
+        }
+    }
+
+    // Unpack, Norm,Exc for the tree's VN
+    ValueNumPair vnpTreeNorm;
+    ValueNumPair vnpTreeExc    = ValueNumStore::VNPForEmptyExcSet();
+    ValueNumPair vnpDivZeroExc = ValueNumStore::VNPForEmptyExcSet();
+    ValueNumPair vnpArithmExc  = ValueNumStore::VNPForEmptyExcSet();
+
+    vnStore->VNPUnpackExc(tree->gtVNPair, &vnpTreeNorm, &vnpTreeExc);
+
+    if (needDivideByZeroExcLib)
+    {
+        vnpDivZeroExc.SetLiberal(
+            vnStore->VNExcSetSingleton(vnStore->VNForFunc(TYP_REF, VNF_DivideByZeroExc, vnOp2NormLib)));
+    }
+    if (needDivideByZeroExcCon)
+    {
+        vnpDivZeroExc.SetConservative(
+            vnStore->VNExcSetSingleton(vnStore->VNForFunc(TYP_REF, VNF_DivideByZeroExc, vnOp2NormCon)));
+    }
+    if (needArithmeticExcLib)
+    {
+        vnpArithmExc.SetLiberal(
+            vnStore->VNExcSetSingleton(vnStore->VNForFunc(TYP_REF, VNF_ArithmeticExc, vnOp1NormLib, vnOp2NormLib)));
+    }
+    if (needArithmeticExcCon)
+    {
+        vnpArithmExc.SetConservative(
+            vnStore->VNExcSetSingleton(vnStore->VNForFunc(TYP_REF, VNF_ArithmeticExc, vnOp1NormLib, vnOp2NormCon)));
+    }
+
+    // Combine vnpDivZeroExc with the exception set of tree
+    ValueNumPair newExcSet = vnStore->VNPExcSetUnion(vnpTreeExc, vnpDivZeroExc);
+    // Combine vnpArithmExc with the newExcSet
+    newExcSet = vnStore->VNPExcSetUnion(newExcSet, vnpArithmExc);
+
+    // Updated VN for tree, it now includes DivideByZeroExc and/or ArithmeticExc
+    tree->gtVNPair = vnStore->VNPWithExc(vnpTreeNorm, newExcSet);
+}
+
+//--------------------------------------------------------------------------------
+// fgValueNumberAddExceptionSetForOverflow
+//         - Adds the exception set for the current tree node
+//           which is performing an overflow checking math operation
+//
+// Arguments:
+//    tree       - The current GenTree node,
+//                 It must be a node that performs an overflow
+//                 checking math operation
+//
+// Return Value:
+//               - The tree's gtVNPair is updated to include the VNF_OverflowExc
+//                 exception set.
+//
+void Compiler::fgValueNumberAddExceptionSetForOverflow(GenTree* tree)
+{
+    assert(tree->gtOverflowEx());
+
+    // We should only be dealing with an Overflow checking ALU operation.
+    VNFunc vnf = GetVNFuncForNode(tree);
+    assert((vnf >= VNF_ADD_OVF) && (vnf <= VNF_MUL_UN_OVF));
+
+    // Unpack, Norm,Exc for the tree's VN
+    //
+    ValueNumPair vnpTreeNorm;
+    ValueNumPair vnpTreeExc = ValueNumStore::VNPForEmptyExcSet();
+
+    vnStore->VNPUnpackExc(tree->gtVNPair, &vnpTreeNorm, &vnpTreeExc);
+
+#ifdef DEBUG
+    // The normal value number function should be the same overflow checking ALU operation as 'vnf'
+    VNFuncApp treeNormFuncApp;
+    assert(vnStore->GetVNFunc(vnpTreeNorm.GetLiberal(), &treeNormFuncApp) && (treeNormFuncApp.m_func == vnf));
+#endif // DEBUG
+
+    // Overflow-checking operations add an overflow exception
+    // The normal result is used as the input argument for the OverflowExc
+    ValueNumPair overflowExcSet =
+        vnStore->VNPExcSetSingleton(vnStore->VNPairForFunc(TYP_REF, VNF_OverflowExc, vnpTreeNorm));
+
+    // Combine the new Overflow exception with the original exception set of tree
+    ValueNumPair newExcSet = vnStore->VNPExcSetUnion(vnpTreeExc, overflowExcSet);
+
+    // Updated VN for tree, it now includes Overflow exception
+    tree->gtVNPair = vnStore->VNPWithExc(vnpTreeNorm, newExcSet);
+}
+
+//--------------------------------------------------------------------------------
+// fgValueNumberAddExceptionSetForCkFinite
+//         - Adds the exception set for the current tree node
+//           which is a CkFinite operation
+//
+// Arguments:
+//    tree       - The current GenTree node,
+//                 It must be a CkFinite node
+//
+// Return Value:
+//               - The tree's gtVNPair is updated to include the VNF_ArithmeticExc
+//                 exception set.
+//
+void Compiler::fgValueNumberAddExceptionSetForCkFinite(GenTree* tree)
+{
+    // We should only be dealing with an check finite operation.
+    assert(tree->OperGet() == GT_CKFINITE);
+
+    // Unpack, Norm,Exc for the tree's VN
+    //
+    ValueNumPair vnpTreeNorm;
+    ValueNumPair vnpTreeExc = ValueNumStore::VNPForEmptyExcSet();
+    ValueNumPair newExcSet;
+
+    vnStore->VNPUnpackExc(tree->gtVNPair, &vnpTreeNorm, &vnpTreeExc);
+
+    // ckfinite adds an Arithmetic exception
+    // The normal result is used as the input argument for the ArithmeticExc
+    ValueNumPair arithmeticExcSet =
+        vnStore->VNPExcSetSingleton(vnStore->VNPairForFunc(TYP_REF, VNF_ArithmeticExc, vnpTreeNorm));
+
+    // Combine the new Arithmetic exception with the original exception set of tree
+    newExcSet = vnStore->VNPExcSetUnion(vnpTreeExc, arithmeticExcSet);
+
+    // Updated VN for tree, it now includes Arithmetic exception
+    tree->gtVNPair = vnStore->VNPWithExc(vnpTreeNorm, newExcSet);
+}
+
+//--------------------------------------------------------------------------------
+// fgValueNumberAddExceptionSet
+//         - Adds any exception sets needed for the current tree node
+//
+// Arguments:
+//    tree       - The current GenTree node,
+//
+// Return Value:
+//               - The tree's gtVNPair is updated to include the exception sets.
+//
+// Notes:        - This method relies upon OperMayTHrow to determine if we need
+//                 to add an exception set.  If OPerMayThrow returns false no
+//                 exception set will be added.
+//
+void Compiler::fgValueNumberAddExceptionSet(GenTree* tree)
+{
+    if (tree->OperMayThrow(this))
+    {
+        switch (tree->OperGet())
+        {
+            case GT_CAST: // A cast with an overflow check
+                break;    // Already handled by VNPairForCast()
+
+            case GT_ADD: // An Overflow checking ALU operation
+            case GT_SUB:
+            case GT_MUL:
+                fgValueNumberAddExceptionSetForOverflow(tree);
+                break;
+
+            case GT_LCLHEAP:
+                // It is not necessary to model the StackOverflow exception for GT_LCLHEAP
+                break;
+
+            case GT_INTRINSIC:
+                // ToDo: model the exceptions for Intrinsics
+                break;
+
+            case GT_IND: // Implicit null check.
+                if ((tree->gtFlags & GTF_IND_ASG_LHS) != 0)
+                {
+                    // Don't add exception set on LHS of assignment
+                    break;
+                }
+            // fall through
+
+            case GT_BLK: // All Block opcodes contain at least one indirection
+            case GT_OBJ:
+            case GT_DYN_BLK:
+            case GT_ARR_LENGTH: // Implicit null check.
+            case GT_NULLCHECK:  // Explicit null check.
+                fgValueNumberAddExceptionSetForIndirection(tree);
+                break;
+
+            case GT_DIV:
+            case GT_UDIV:
+            case GT_MOD:
+            case GT_UMOD:
+                fgValueNumberAddExceptionSetForDivision(tree);
+                break;
+
+            case GT_CKFINITE:
+                fgValueNumberAddExceptionSetForCkFinite(tree);
+                break;
+
+            default:
+                assert(!"Handle this oper in fgValueNumberAddExceptionSet");
+                break;
+        }
+    }
+}
+
 #ifdef DEBUG
 // This method asserts that SSA name constraints specified are satisfied.
 // Until we figure out otherwise, all VN's are assumed to be liberal.
index cc21168..d09863d 100644 (file)
@@ -190,6 +190,9 @@ private:
     // Returns "true" iff "vnf" can be evaluated for constant arguments.
     static bool CanEvalForConstantArgs(VNFunc vnf);
 
+    // Returns "true" iff "vnf" should be folded by evaluating the func with constant arguments.
+    bool VNEvalShouldFold(var_types typ, VNFunc func, ValueNum arg0VN, ValueNum arg1VN);
+
     // return vnf(v0)
     template <typename T>
     static T EvalOp(VNFunc vnf, T v0);
@@ -240,6 +243,8 @@ private:
     ValueNum EvalFuncForConstantFPArgs(var_types typ, VNFunc vnf, ValueNum vn0, ValueNum vn1);
     ValueNum EvalCastForConstantArgs(var_types typ, VNFunc vnf, ValueNum vn0, ValueNum vn1);
 
+    ValueNum EvalUsingMathIdentity(var_types typ, VNFunc vnf, ValueNum vn0, ValueNum vn1);
+
 // This is the constant value used for the default value of m_mapSelectBudget
 #define DEFAULT_MAP_SELECT_BUDGET 100 // used by JitVNMapSelBudget
 
@@ -452,15 +457,14 @@ public:
     // True "iff" vn is a value returned by a call to a shared static helper.
     bool IsSharedStatic(ValueNum vn);
 
-    // VN's for functions of other values.
-    // Four overloads, for arities 0, 1, 2, and 3.  If we need other arities, we'll consider it.
+    // VNForFunc: We have five overloads, for arities 0, 1, 2, 3 and 4
     ValueNum VNForFunc(var_types typ, VNFunc func);
     ValueNum VNForFunc(var_types typ, VNFunc func, ValueNum opVNwx);
     // This must not be used for VNF_MapSelect applications; instead use VNForMapSelect, below.
     ValueNum VNForFunc(var_types typ, VNFunc func, ValueNum op1VNwx, ValueNum op2VNwx);
     ValueNum VNForFunc(var_types typ, VNFunc func, ValueNum op1VNwx, ValueNum op2VNwx, ValueNum op3VNwx);
 
-    // The following four op VNForFunc is only used for VNF_PtrToArrElem, elemTypeEqVN, arrVN, inxVN, fldSeqVN
+    // The following four-op VNForFunc is used for VNF_PtrToArrElem, elemTypeEqVN, arrVN, inxVN, fldSeqVN
     ValueNum VNForFunc(
         var_types typ, VNFunc func, ValueNum op1VNwx, ValueNum op2VNwx, ValueNum op3VNwx, ValueNum op4VNwx);
 
index a5a6b13..e29721b 100644 (file)
@@ -56,16 +56,15 @@ ValueNumFuncDef(ExcSetCons, 2, false, false, false)         // Args: 0: exceptio
 // Curremtly  when the execution is always thrown, the value VNForVoid() is used as Arg0 by OverflowExc and DivideByZeroExc
 // 
 ValueNumFuncDef(NullPtrExc, 1, false, false, false)         // Null pointer exception check.  Args: 0: address value,  throws when it is null
-ValueNumFuncDef(ArithmeticExc, 2, false, false, false)      // E.g., for signed its, MinInt / -1.
-ValueNumFuncDef(OverflowExc, 1, false, false, false)        // Integer overflow check. Args: 0: expression value,  throws when it overflows
+ValueNumFuncDef(ArithmeticExc, 2, false, false, false)      // Arithmetic exception check, ckfinite and integer division overflow, Args: 0: expression value,
+ValueNumFuncDef(OverflowExc, 1, false, false, false)        // Integer overflow check. used for checked add,sub and mul Args: 0: expression value,  throws when it overflows
 ValueNumFuncDef(ConvOverflowExc, 2, false, false, false)    // Cast conversion overflow check.  Args: 0: input value; 1: var_types of the target type
-                                                            // (shifted left one bit; low bit encode whether source is unsigned.) 
+                                                            // (shifted left one bit; low bit encode whether source is unsigned.) 
 ValueNumFuncDef(DivideByZeroExc, 1, false, false, false)    // Division by zero check.  Args: 0: divisor value, throws when it is zero
-ValueNumFuncDef(IndexOutOfRangeExc, 2, false, false, false) // Args: 0: array length; 1: index value, throws when the bounds check fails.
-ValueNumFuncDef(InvalidCastExc, 2, false, false, false)     // Args: 0: ref value being cast; 1: handle of type being cast to.  Represents the exception thrown if the cast fails.
+ValueNumFuncDef(IndexOutOfRangeExc, 2, false, false, false) // Array bounds check, Args: 0: array length; 1: index value, throws when the bounds check fails.
+ValueNumFuncDef(InvalidCastExc, 2, false, false, false)     // CastClass check, Args: 0: ref value being cast; 1: handle of type being cast to, throws when the cast fails.
 ValueNumFuncDef(NewArrOverflowExc, 1, false, false, false)  // Raises Integer overflow when Arg 0 is negative
-ValueNumFuncDef(HelperMultipleExc, 0, false, false, false)  // Represents one or more different exceptions that may be thrown by a JitHelper
-
+ValueNumFuncDef(HelperMultipleExc, 0, false, false, false)  // Represents one or more different exceptions that could be thrown by a Jit Helper method
 
 ValueNumFuncDef(Lng2Dbl, 1, false, false, false)
 ValueNumFuncDef(ULng2Dbl, 1, false, false, false)