Introduce `MemoryKind` abstraction
authorJoseph Tremoulet <jotrem@microsoft.com>
Thu, 29 Dec 2016 22:02:18 +0000 (17:02 -0500)
committerJoseph Tremoulet <jotrem@microsoft.com>
Wed, 8 Feb 2017 14:32:46 +0000 (09:32 -0500)
Re-cast the notion of "heap" (in liveness, SSA, and value-numbering) as
one of potentially many `MemoryKind`s, called `GcHeap`.  Update names,
comments, data structures, and signatures as appropriate to parameterize
relevant data/methods over `MemoryKind`.  This change is a no-diff
refactoring, and currently `GcHeap` is the only `MemoryKind`.  Generally,
codepaths which will generically need to process all `MemoryKinds`s
(initializing, dumping, dataflow propagation) now iterate over all
`MemoryKinds`, and codepaths which are sensitive to the semantics of the
specific `MemoryKind` (def/use identification in liveness and value
numbering) are changed to specifically operate on `MemoryKind::GcHeap`.

One notable exception is that `lvMemoryPerSsaData` and `CountForMemoryDef`
are *not* parameterized over `MemoryKind`; there's a single "space" of SSA
defnums for memory defs (though the same tree can incur different defs for
different memory kinds [in which case their defnums will differ]), to
facilitate subsequently sharing SSA nodes across memory kinds when
appropriate.

18 files changed:
src/jit/block.cpp
src/jit/block.h
src/jit/codegenlegacy.cpp
src/jit/compiler.cpp
src/jit/compiler.h
src/jit/compiler.hpp
src/jit/compmemkind.h
src/jit/gentree.cpp
src/jit/liveness.cpp
src/jit/optimizer.cpp
src/jit/ssabuilder.cpp
src/jit/ssabuilder.h
src/jit/ssarenamestate.cpp
src/jit/ssarenamestate.h
src/jit/valuenum.cpp
src/jit/valuenum.h
src/jit/valuenumfuncs.h
src/jit/valuenumtype.h

index bb6a57c..6d8bc34 100644 (file)
@@ -572,10 +572,10 @@ void BasicBlock::dspBlockHeader(Compiler* compiler,
 
 #endif // DEBUG
 
-// Allocation function for HeapPhiArg.
-void* BasicBlock::HeapPhiArg::operator new(size_t sz, Compiler* comp)
+// Allocation function for MemoryPhiArg.
+void* BasicBlock::MemoryPhiArg::operator new(size_t sz, Compiler* comp)
 {
-    return comp->compGetMem(sz, CMK_HeapPhiArg);
+    return comp->compGetMem(sz, CMK_MemoryPhiArg);
 }
 
 //------------------------------------------------------------------------
@@ -773,7 +773,7 @@ BasicBlock* BasicBlock::GetUniqueSucc()
 }
 
 // Static vars.
-BasicBlock::HeapPhiArg* BasicBlock::EmptyHeapPhiDef = (BasicBlock::HeapPhiArg*)0x1;
+BasicBlock::MemoryPhiArg* BasicBlock::EmptyMemoryPhiDef = (BasicBlock::MemoryPhiArg*)0x1;
 
 unsigned PtrKeyFuncs<BasicBlock>::GetHashCode(const BasicBlock* ptr)
 {
index 3d45ea0..d95eb9c 100644 (file)
@@ -144,6 +144,86 @@ struct EntryState
     StackEntry* esStack;               // ptr to  stack
 };
 
+// Enumeration of the kinds of memory whose state changes the compiler tracks
+enum MemoryKind
+{
+    GcHeap = 0,      // Includes actual GC heap, and also static fields
+    MemoryKindCount, // Number of MemoryKinds
+};
+#ifdef DEBUG
+const char* const memoryKindNames[] = {"GcHeap"};
+#endif // DEBUG
+
+// Bitmask describing a set of memory kinds (usable in bitfields)
+typedef unsigned int MemoryKindSet;
+
+// Bitmask for a MemoryKindSet containing just the specified MemoryKind
+inline MemoryKindSet memoryKindSet(MemoryKind memoryKind)
+{
+    return (1U << memoryKind);
+}
+
+// Bitmask for a MemoryKindSet containing the specified MemoryKinds
+template <typename... MemoryKinds>
+inline MemoryKindSet memoryKindSet(MemoryKind memoryKind, MemoryKinds... memoryKinds)
+{
+    return memoryKindSet(memoryKind) | memoryKindSet(memoryKinds...);
+}
+
+// Bitmask containing all the MemoryKinds
+const MemoryKindSet fullMemoryKindSet = (1 << MemoryKindCount) - 1;
+
+// Bitmask containing no MemoryKinds
+const MemoryKindSet emptyMemoryKindSet = 0;
+
+// Standard iterator class for iterating through MemoryKinds
+class MemoryKindIterator
+{
+    int value;
+
+public:
+    explicit inline MemoryKindIterator(int val) : value(val)
+    {
+    }
+    inline MemoryKindIterator& operator++()
+    {
+        ++value;
+        return *this;
+    }
+    inline MemoryKindIterator operator++(int)
+    {
+        return MemoryKindIterator(value++);
+    }
+    inline MemoryKind operator*()
+    {
+        return static_cast<MemoryKind>(value);
+    }
+    friend bool operator==(const MemoryKindIterator& left, const MemoryKindIterator& right)
+    {
+        return left.value == right.value;
+    }
+    friend bool operator!=(const MemoryKindIterator& left, const MemoryKindIterator& right)
+    {
+        return left.value != right.value;
+    }
+};
+
+// Empty struct that allows enumerating memory kinds via `for(MemoryKind kind : allMemoryKinds())`
+struct allMemoryKinds
+{
+    inline allMemoryKinds()
+    {
+    }
+    inline MemoryKindIterator begin()
+    {
+        return MemoryKindIterator(0);
+    }
+    inline MemoryKindIterator end()
+    {
+        return MemoryKindIterator(MemoryKindCount);
+    }
+};
+
 // This encapsulates the "exception handling" successors of a block.  That is,
 // if a basic block BB1 occurs in a try block, we consider the first basic block
 // BB2 of the corresponding handler to be an "EH successor" of BB1.  Because we
@@ -808,41 +888,42 @@ struct BasicBlock : private LIR::Range
     VARSET_TP bbLiveIn;  // variables live on entry
     VARSET_TP bbLiveOut; // variables live on exit
 
-    // Use, def, live in/out information for the implicit "Heap" variable.
-    unsigned bbHeapUse : 1; // must be set to true for any block that references the global Heap
-    unsigned bbHeapDef : 1; // must be set to true for any block that mutates the global Heap
-    unsigned bbHeapLiveIn : 1;
-    unsigned bbHeapLiveOut : 1;
-    unsigned bbHeapHavoc : 1; // If true, at some point the block does an operation that leaves the heap
-                              // in an unknown state. (E.g., unanalyzed call, store through unknown
-                              // pointer...)
+    // Use, def, live in/out information for the implicit memory variable.
+    MemoryKindSet bbMemoryUse : MemoryKindCount; // must be set for any MemoryKinds this block references
+    MemoryKindSet bbMemoryDef : MemoryKindCount; // must be set for any MemoryKinds this block mutates
+    MemoryKindSet bbMemoryLiveIn : MemoryKindCount;
+    MemoryKindSet bbMemoryLiveOut : MemoryKindCount;
+    MemoryKindSet bbMemoryHavoc : MemoryKindCount; // If true, at some point the block does an operation
+                                                   // that leaves memory in an unknown state. (E.g.,
+                                                   // unanalyzed call, store through unknown pointer...)
 
-    // We want to make phi functions for the special implicit var "Heap".  But since this is not a real
+    // We want to make phi functions for the special implicit var memory.  But since this is not a real
     // lclVar, and thus has no local #, we can't use a GenTreePhiArg.  Instead, we use this struct.
-    struct HeapPhiArg
+    struct MemoryPhiArg
     {
-        unsigned    m_ssaNum;  // SSA# for incoming value.
-        HeapPhiArg* m_nextArg; // Next arg in the list, else NULL.
+        unsigned      m_ssaNum;  // SSA# for incoming value.
+        MemoryPhiArg* m_nextArg; // Next arg in the list, else NULL.
 
         unsigned GetSsaNum()
         {
             return m_ssaNum;
         }
 
-        HeapPhiArg(unsigned ssaNum, HeapPhiArg* nextArg = nullptr) : m_ssaNum(ssaNum), m_nextArg(nextArg)
+        MemoryPhiArg(unsigned ssaNum, MemoryPhiArg* nextArg = nullptr) : m_ssaNum(ssaNum), m_nextArg(nextArg)
         {
         }
 
         void* operator new(size_t sz, class Compiler* comp);
     };
-    static HeapPhiArg* EmptyHeapPhiDef; // Special value (0x1, FWIW) to represent a to-be-filled in Phi arg list
-                                        // for Heap.
-    HeapPhiArg* bbHeapSsaPhiFunc;       // If the "in" Heap SSA var is not a phi definition, this value is NULL.
-                                        // Otherwise, it is either the special value EmptyHeapPhiDefn, to indicate
-                                        // that Heap needs a phi definition on entry, or else it is the linked list
-                                        // of the phi arguments.
-    unsigned bbHeapSsaNumIn;            // The SSA # of "Heap" on entry to the block.
-    unsigned bbHeapSsaNumOut;           // The SSA # of "Heap" on exit from the block.
+    static MemoryPhiArg* EmptyMemoryPhiDef; // Special value (0x1, FWIW) to represent a to-be-filled in Phi arg list
+                                            // for Heap.
+    MemoryPhiArg* bbMemorySsaPhiFunc[MemoryKindCount]; // If the "in" Heap SSA var is not a phi definition, this value
+                                                       // is NULL.
+    // Otherwise, it is either the special value EmptyMemoryPhiDefn, to indicate
+    // that Heap needs a phi definition on entry, or else it is the linked list
+    // of the phi arguments.
+    unsigned bbMemorySsaNumIn[MemoryKindCount];  // The SSA # of memory on entry to the block.
+    unsigned bbMemorySsaNumOut[MemoryKindCount]; // The SSA # of memory on exit from the block.
 
     VARSET_TP bbScope; // variables in scope over the block
 
index c249aeb..a57953a 100644 (file)
@@ -20664,17 +20664,17 @@ GenTreePtr Compiler::fgLegacyPerStatementLocalVarLiveness(GenTreePtr startNode,
     VARSET_TP VARSET_INIT(this, defSet_BeforeSplit, fgCurDefSet); // Store the current fgCurDefSet and fgCurUseSet so
     VARSET_TP VARSET_INIT(this, useSet_BeforeSplit, fgCurUseSet); // we can restore then before entering the elseTree.
 
-    bool heapUse_BeforeSplit   = fgCurHeapUse;
-    bool heapDef_BeforeSplit   = fgCurHeapDef;
-    bool heapHavoc_BeforeSplit = fgCurHeapHavoc;
+    MemoryKindSet memoryUse_BeforeSplit   = fgCurMemoryUse;
+    MemoryKindSet memoryDef_BeforeSplit   = fgCurMemoryDef;
+    MemoryKindSet memoryHavoc_BeforeSplit = fgCurMemoryHavoc;
 
     VARSET_TP VARSET_INIT_NOCOPY(defSet_AfterThenTree, VarSetOps::MakeEmpty(this)); // These two variables will store
                                                                                     // the USE and DEF sets after
     VARSET_TP VARSET_INIT_NOCOPY(useSet_AfterThenTree, VarSetOps::MakeEmpty(this)); // evaluating the thenTree.
 
-    bool heapUse_AfterThenTree   = fgCurHeapUse;
-    bool heapDef_AfterThenTree   = fgCurHeapDef;
-    bool heapHavoc_AfterThenTree = fgCurHeapHavoc;
+    MemoryKindSet memoryUse_AfterThenTree   = fgCurMemoryUse;
+    MemoryKindSet memoryDef_AfterThenTree   = fgCurMemoryDef;
+    MemoryKindSet memoryHavoc_AfterThenTree = fgCurMemoryHavoc;
 
     // relopNode is either NULL or a GTF_RELOP_QMARK node.
     assert(!relopNode || (relopNode->OperKind() & GTK_RELOP) && (relopNode->gtFlags & GTF_RELOP_QMARK));
@@ -20701,9 +20701,9 @@ GenTreePtr Compiler::fgLegacyPerStatementLocalVarLiveness(GenTreePtr startNode,
                 VarSetOps::IntersectionD(this, fgCurDefSet, defSet_AfterThenTree);
                 VarSetOps::UnionD(this, fgCurUseSet, useSet_AfterThenTree);
 
-                fgCurHeapDef   = fgCurHeapDef && heapDef_AfterThenTree;
-                fgCurHeapHavoc = fgCurHeapHavoc && heapHavoc_AfterThenTree;
-                fgCurHeapUse   = fgCurHeapUse || heapUse_AfterThenTree;
+                fgCurMemoryDef   = fgCurMemoryDef & memoryDef_AfterThenTree;
+                fgCurMemoryHavoc = fgCurMemoryHavoc & memoryHavoc_AfterThenTree;
+                fgCurMemoryUse   = fgCurMemoryUse | memoryUse_AfterThenTree;
 
                 // Return the GT_QMARK node itself so the caller can continue from there.
                 // NOTE: the caller will get to the next node by doing the "tree = tree->gtNext"
@@ -20720,16 +20720,16 @@ GenTreePtr Compiler::fgLegacyPerStatementLocalVarLiveness(GenTreePtr startNode,
                 VarSetOps::Assign(this, defSet_AfterThenTree, fgCurDefSet);
                 VarSetOps::Assign(this, useSet_AfterThenTree, fgCurUseSet);
 
-                heapDef_AfterThenTree   = fgCurHeapDef;
-                heapHavoc_AfterThenTree = fgCurHeapHavoc;
-                heapUse_AfterThenTree   = fgCurHeapUse;
+                memoryDef_AfterThenTree   = fgCurMemoryDef;
+                memoryHavoc_AfterThenTree = fgCurMemoryHavoc;
+                memoryUse_AfterThenTree   = fgCurMemoryUse;
 
                 VarSetOps::Assign(this, fgCurDefSet, defSet_BeforeSplit);
                 VarSetOps::Assign(this, fgCurUseSet, useSet_BeforeSplit);
 
-                fgCurHeapDef   = heapDef_BeforeSplit;
-                fgCurHeapHavoc = heapHavoc_BeforeSplit;
-                fgCurHeapUse   = heapUse_BeforeSplit;
+                fgCurMemoryDef   = memoryDef_BeforeSplit;
+                fgCurMemoryHavoc = memoryHavoc_BeforeSplit;
+                fgCurMemoryUse   = memoryUse_BeforeSplit;
 
                 break;
 
@@ -20743,35 +20743,35 @@ GenTreePtr Compiler::fgLegacyPerStatementLocalVarLiveness(GenTreePtr startNode,
                 break;
 
             case GT_CLS_VAR:
-                // For Volatile indirection, first mutate the global heap
+                // For Volatile indirection, first mutate GcHeap
                 // see comments in ValueNum.cpp (under case GT_CLS_VAR)
                 // This models Volatile reads as def-then-use of the heap.
                 // and allows for a CSE of a subsequent non-volatile read
                 if ((tree->gtFlags & GTF_FLD_VOLATILE) != 0)
                 {
                     // For any Volatile indirection, we must handle it as a
-                    // definition of the global heap
-                    fgCurHeapDef = true;
+                    // definition of GcHeap
+                    fgCurMemoryDef |= memoryKindSet(GcHeap);
                 }
                 // If the GT_CLS_VAR is the lhs of an assignment, we'll handle it as a heap def, when we get to
                 // assignment.
                 // Otherwise, we treat it as a use here.
                 if ((tree->gtFlags & GTF_CLS_VAR_ASG_LHS) == 0)
                 {
-                    fgCurHeapUse = true;
+                    fgCurMemoryUse |= memoryKindSet(GcHeap);
                 }
                 break;
 
             case GT_IND:
-                // For Volatile indirection, first mutate the global heap
+                // For Volatile indirection, first mutate GcHeap
                 // see comments in ValueNum.cpp (under case GT_CLS_VAR)
                 // This models Volatile reads as def-then-use of the heap.
                 // and allows for a CSE of a subsequent non-volatile read
                 if ((tree->gtFlags & GTF_IND_VOLATILE) != 0)
                 {
                     // For any Volatile indirection, we must handle it as a
-                    // definition of the global heap
-                    fgCurHeapDef = true;
+                    // definition of GcHeap
+                    fgCurMemoryDef |= memoryKindSet(GcHeap);
                 }
 
                 // If the GT_IND is the lhs of an assignment, we'll handle it
@@ -20784,7 +20784,7 @@ GenTreePtr Compiler::fgLegacyPerStatementLocalVarLiveness(GenTreePtr startNode,
                     GenTreePtr           addrArg         = tree->gtOp.gtOp1->gtEffectiveVal(/*commaOnly*/ true);
                     if (!addrArg->DefinesLocalAddr(this, /*width doesn't matter*/ 0, &dummyLclVarTree, &dummyIsEntire))
                     {
-                        fgCurHeapUse = true;
+                        fgCurMemoryUse |= memoryKindSet(GcHeap);
                     }
                     else
                     {
@@ -20801,22 +20801,22 @@ GenTreePtr Compiler::fgLegacyPerStatementLocalVarLiveness(GenTreePtr startNode,
                 unreached();
                 break;
 
-            // We'll assume these are use-then-defs of the heap.
+            // We'll assume these are use-then-defs of GcHeap.
             case GT_LOCKADD:
             case GT_XADD:
             case GT_XCHG:
             case GT_CMPXCHG:
-                fgCurHeapUse   = true;
-                fgCurHeapDef   = true;
-                fgCurHeapHavoc = true;
+                fgCurMemoryUse |= memoryKindSet(GcHeap);
+                fgCurMemoryDef |= memoryKindSet(GcHeap);
+                fgCurMemoryHavoc |= memoryKindSet(GcHeap);
                 break;
 
             case GT_MEMORYBARRIER:
-                // Simliar to any Volatile indirection, we must handle this as a definition of the global heap
-                fgCurHeapDef = true;
+                // Simliar to any Volatile indirection, we must handle this as a definition of GcHeap
+                fgCurMemoryDef |= memoryKindSet(GcHeap);
                 break;
 
-            // For now, all calls read/write the heap, the latter in its entirety.  Might tighten this case later.
+            // For now, all calls read/write GcHeap, the latter in its entirety.  Might tighten this case later.
             case GT_CALL:
             {
                 GenTreeCall* call    = tree->AsCall();
@@ -20832,9 +20832,9 @@ GenTreePtr Compiler::fgLegacyPerStatementLocalVarLiveness(GenTreePtr startNode,
                 }
                 if (modHeap)
                 {
-                    fgCurHeapUse   = true;
-                    fgCurHeapDef   = true;
-                    fgCurHeapHavoc = true;
+                    fgCurMemoryUse |= memoryKindSet(GcHeap);
+                    fgCurMemoryDef |= memoryKindSet(GcHeap);
+                    fgCurMemoryHavoc |= memoryKindSet(GcHeap);
                 }
             }
 
@@ -20866,14 +20866,14 @@ GenTreePtr Compiler::fgLegacyPerStatementLocalVarLiveness(GenTreePtr startNode,
 
             default:
 
-                // Determine whether it defines a heap location.
+                // Determine whether it defines a GC heap location.
                 if (tree->OperIsAssignment() || tree->OperIsBlkOp())
                 {
                     GenTreeLclVarCommon* dummyLclVarTree = NULL;
                     if (!tree->DefinesLocal(this, &dummyLclVarTree))
                     {
-                        // If it doesn't define a local, then it might update the heap.
-                        fgCurHeapDef = true;
+                        // If it doesn't define a local, then it might update the GC heap.
+                        fgCurMemoryDef |= memoryKindSet(GcHeap);
                     }
                 }
 
index 2d03085..3b1db5a 100644 (file)
@@ -1639,12 +1639,12 @@ void Compiler::compDisplayStaticSizes(FILE* fout)
             sizeof(bbDummy->bbLiveIn));
     fprintf(fout, "Offset / size of bbLiveOut             = %3u / %3u\n", offsetof(BasicBlock, bbLiveOut),
             sizeof(bbDummy->bbLiveOut));
-    fprintf(fout, "Offset / size of bbHeapSsaPhiFunc      = %3u / %3u\n", offsetof(BasicBlock, bbHeapSsaPhiFunc),
-            sizeof(bbDummy->bbHeapSsaPhiFunc));
-    fprintf(fout, "Offset / size of bbHeapSsaNumIn        = %3u / %3u\n", offsetof(BasicBlock, bbHeapSsaNumIn),
-            sizeof(bbDummy->bbHeapSsaNumIn));
-    fprintf(fout, "Offset / size of bbHeapSsaNumOut       = %3u / %3u\n", offsetof(BasicBlock, bbHeapSsaNumOut),
-            sizeof(bbDummy->bbHeapSsaNumOut));
+    fprintf(fout, "Offset / size of bbMemorySsaPhiFunc      = %3u / %3u\n", offsetof(BasicBlock, bbMemorySsaPhiFunc),
+            sizeof(bbDummy->bbMemorySsaPhiFunc));
+    fprintf(fout, "Offset / size of bbMemorySsaNumIn        = %3u / %3u\n", offsetof(BasicBlock, bbMemorySsaNumIn),
+            sizeof(bbDummy->bbMemorySsaNumIn));
+    fprintf(fout, "Offset / size of bbMemorySsaNumOut       = %3u / %3u\n", offsetof(BasicBlock, bbMemorySsaNumOut),
+            sizeof(bbDummy->bbMemorySsaNumOut));
     fprintf(fout, "Offset / size of bbScope               = %3u / %3u\n", offsetof(BasicBlock, bbScope),
             sizeof(bbDummy->bbScope));
     fprintf(fout, "Offset / size of bbCseGen              = %3u / %3u\n", offsetof(BasicBlock, bbCseGen),
@@ -1786,9 +1786,9 @@ void Compiler::compInit(ArenaAllocator* pAlloc, InlineInfo* inlineInfo)
         impSpillCliquePredMembers = ExpandArray<BYTE>(getAllocator());
         impSpillCliqueSuccMembers = ExpandArray<BYTE>(getAllocator());
 
-        memset(&lvHeapPerSsaData, 0, sizeof(PerSsaArray));
-        lvHeapPerSsaData.Init(getAllocator());
-        lvHeapNumSsaNames = 0;
+        memset(&lvMemoryPerSsaData, 0, sizeof(PerSsaArray));
+        lvMemoryPerSsaData.Init(getAllocator());
+        lvMemoryNumSsaNames = 0;
 
         //
         // Initialize all the per-method statistics gathering data structures.
@@ -1869,8 +1869,11 @@ void Compiler::compInit(ArenaAllocator* pAlloc, InlineInfo* inlineInfo)
     m_fieldSeqStore      = nullptr;
     m_zeroOffsetFieldMap = nullptr;
     m_arrayInfoMap       = nullptr;
-    m_heapSsaMap         = nullptr;
     m_refAnyClass        = nullptr;
+    for (MemoryKind memoryKind : allMemoryKinds())
+    {
+        m_memorySsaMap[memoryKind] = nullptr;
+    }
 
 #ifdef DEBUG
     if (!compIsForInlining())
index 7698731..4862499 100644 (file)
@@ -2749,21 +2749,21 @@ protected:
     static fgWalkPreFn lvaMarkLclRefsCallback;
     void lvaMarkLclRefs(GenTreePtr tree);
 
-    // Keeps the mapping from SSA #'s to VN's for the implicit "Heap" variable.
-    PerSsaArray lvHeapPerSsaData;
-    unsigned    lvHeapNumSsaNames;
+    // Keeps the mapping from SSA #'s to VN's for the implicit memory variables.
+    PerSsaArray lvMemoryPerSsaData;
+    unsigned    lvMemoryNumSsaNames;
 
 public:
-    // Returns the address of the per-Ssa data for "Heap" at the given ssaNum (which is required
+    // Returns the address of the per-Ssa data for memory at the given ssaNum (which is required
     // not to be the SsaConfig::RESERVED_SSA_NUM, which indicates that the variable is
     // not an SSA variable).
-    LclSsaVarDsc* GetHeapPerSsaData(unsigned ssaNum)
+    LclSsaVarDsc* GetMemoryPerSsaData(unsigned ssaNum)
     {
         assert(ssaNum != SsaConfig::RESERVED_SSA_NUM);
         assert(SsaConfig::RESERVED_SSA_NUM == 0);
         ssaNum--;
-        assert(ssaNum < lvHeapNumSsaNames);
-        return &lvHeapPerSsaData.GetRef(ssaNum);
+        assert(ssaNum < lvMemoryNumSsaNames);
+        return &lvMemoryPerSsaData.GetRef(ssaNum);
     }
 
     /*
@@ -3804,7 +3804,7 @@ public:
     // tree node).
     void fgValueNumber();
 
-    // Computes new heap VN via the assignment H[elemTypeEq][arrVN][inx][fldSeq] = rhsVN.
+    // Computes new GcHeap VN via the assignment H[elemTypeEq][arrVN][inx][fldSeq] = rhsVN.
     // Assumes that "elemTypeEq" is the (equivalence class rep) of the array element type.
     // The 'indType' is the indirection type of the lhs of the assignment and will typically
     // match the element type of the array or fldSeq.  When this type doesn't match
@@ -3835,7 +3835,7 @@ public:
 
     // Requires "funcApp" to be a VNF_PtrToArrElem, and "addrXvn" to represent the exception set thrown
     // by evaluating the array index expression "tree".  Returns the value number resulting from
-    // dereferencing the array in the current heap state.  If "tree" is non-null, it must be the
+    // dereferencing the array in the current GcHeap state.  If "tree" is non-null, it must be the
     // "GT_IND" that does the dereference, and it is given the returned value number.
     ValueNum fgValueNumberArrIndexVal(GenTreePtr tree, struct VNFuncApp* funcApp, ValueNum addrXvn);
 
@@ -3848,18 +3848,18 @@ public:
 
     // Requires that "entryBlock" is the entry block of loop "loopNum", and that "loopNum" is the
     // innermost loop of which "entryBlock" is the entry.  Returns the value number that should be
-    // assumed for the heap at the start "entryBlk".
-    ValueNum fgHeapVNForLoopSideEffects(BasicBlock* entryBlock, unsigned loopNum);
+    // assumed for the memoryKind at the start "entryBlk".
+    ValueNum fgMemoryVNForLoopSideEffects(MemoryKind memoryKind, BasicBlock* entryBlock, unsigned loopNum);
 
-    // Called when an operation (performed by "tree", described by "msg") may cause the global Heap to be mutated.
-    void fgMutateHeap(GenTreePtr tree DEBUGARG(const char* msg));
+    // Called when an operation (performed by "tree", described by "msg") may cause the GcHeap to be mutated.
+    void fgMutateGcHeap(GenTreePtr tree DEBUGARG(const char* msg));
 
-    // For a store at curTree, ecord the new heapVN in curHeapVN and curTree's HeapSsaMap entry.
-    void recordHeapStore(GenTreePtr curTree, ValueNum heapVN DEBUGARG(const char* msg));
+    // For a GC heap store at curTree, record the new curHeapVN and update curTree's MemorySsaMap.
+    void recordGcHeapStore(GenTreePtr curTree, ValueNum gcHeapVN DEBUGARG(const char* msg));
 
-    // Tree caused an update in the current heap VN.  If "tree" has an associated heap SSA #, record that
+    // Tree caused an update in the current memory VN.  If "tree" has an associated heap SSA #, record that
     // value in that SSA #.
-    void fgValueNumberRecordHeapSsa(GenTreePtr tree);
+    void fgValueNumberRecordMemorySsa(MemoryKind memoryKind, GenTreePtr tree);
 
     // The input 'tree' is a leaf node that is a constant
     // Assign the proper value number to the tree
@@ -3898,11 +3898,11 @@ public:
     // Requires "helpFunc" to be pure.  Returns the corresponding VNFunc.
     VNFunc fgValueNumberHelperMethVNFunc(CorInfoHelpFunc helpFunc);
 
-    // This is the current value number for the "Heap" implicit variable while
-    // doing value numbering.  This is the value number under the "liberal" interpretation
-    // of heap values; the "conservative" interpretation needs no VN, since every access of
-    // the heap yields an unknown value.
-    ValueNum fgCurHeapVN;
+    // These are the current value number for the memory implicit variables while
+    // doing value numbering.  These are the value numbers under the "liberal" interpretation
+    // of memory values; the "conservative" interpretation needs no VN, since every access of
+    // memory yields an unknown value.
+    ValueNum fgCurMemoryVN[MemoryKindCount];
 
     // Return a "pseudo"-class handle for an array element type.  If "elemType" is TYP_STRUCT,
     // requires "elemStructType" to be non-null (and to have a low-order zero).  Otherwise, low order bit
@@ -4674,9 +4674,9 @@ private:
     VARSET_TP fgCurUseSet; // vars used     by block (before an assignment)
     VARSET_TP fgCurDefSet; // vars assigned by block (before a use)
 
-    bool fgCurHeapUse;   // True iff the current basic block uses the heap before defining it.
-    bool fgCurHeapDef;   // True iff the current basic block defines the heap.
-    bool fgCurHeapHavoc; // True if  the current basic block is known to set the heap to a "havoc" value.
+    MemoryKindSet fgCurMemoryUse;   // True iff the current basic block uses memory.
+    MemoryKindSet fgCurMemoryDef;   // True iff the current basic block modifies memory.
+    MemoryKindSet fgCurMemoryHavoc; // True if  the current basic block is known to set memory to a "havoc" value.
 
     void fgMarkUseDef(GenTreeLclVarCommon* tree);
 
@@ -5029,9 +5029,10 @@ public:
 #define LPFLG_ASGVARS_INC 0x8000 // "lpAsgVars" is incomplete -- vars beyond those representable in an AllVarSet
                                  // type are assigned to.
 
-        bool lpLoopHasHeapHavoc; // The loop contains an operation that we assume has arbitrary heap side effects.
-                                 // If this is set, the fields below may not be accurate (since they become irrelevant.)
-        bool lpContainsCall;     // True if executing the loop body *may* execute a call
+        bool lpLoopHasMemoryHavoc[MemoryKindCount]; // The loop contains an operation that we assume has arbitrary
+                                                    // memory side effects.  If this is set, the fields below
+                                                    // may not be accurate (since they become irrelevant.)
+        bool lpContainsCall;                        // True if executing the loop body *may* execute a call
 
         VARSET_TP lpVarInOut;  // The set of variables that are IN or OUT during the execution of this loop
         VARSET_TP lpVarUseDef; // The set of variables that are USE or DEF during the execution of this loop
@@ -9028,21 +9029,22 @@ public:
         return compRoot->m_arrayInfoMap;
     }
 
-    NodeToUnsignedMap* m_heapSsaMap;
+    NodeToUnsignedMap* m_memorySsaMap[MemoryKindCount];
 
-    // In some cases, we want to assign intermediate SSA #'s to heap states, and know what nodes create those heap
-    // states. (We do this for try blocks, where, if the try block doesn't do a call that loses track of the heap state,
-    // all the possible heap states are possible initial states of the corresponding catch block(s).)
-    NodeToUnsignedMap* GetHeapSsaMap()
+    // In some cases, we want to assign intermediate SSA #'s to memory states, and know what nodes create those memory
+    // states. (We do this for try blocks, where, if the try block doesn't do a call that loses track of the memory
+    // state, all the possible memory states are possible initial states of the corresponding catch block(s).)
+    NodeToUnsignedMap* GetMemorySsaMap(MemoryKind memoryKind)
     {
+        assert(memoryKind < MemoryKindCount);
         Compiler* compRoot = impInlineRoot();
-        if (compRoot->m_heapSsaMap == nullptr)
+        if (compRoot->m_memorySsaMap[memoryKind] == nullptr)
         {
             // Create a CompAllocator that labels sub-structure with CMK_ArrayInfoMap, and use that for allocation.
-            IAllocator* ialloc     = new (this, CMK_ArrayInfoMap) CompAllocator(this, CMK_ArrayInfoMap);
-            compRoot->m_heapSsaMap = new (ialloc) NodeToUnsignedMap(ialloc);
+            IAllocator* ialloc                   = new (this, CMK_ArrayInfoMap) CompAllocator(this, CMK_ArrayInfoMap);
+            compRoot->m_memorySsaMap[memoryKind] = new (ialloc) NodeToUnsignedMap(ialloc);
         }
-        return compRoot->m_heapSsaMap;
+        return compRoot->m_memorySsaMap[memoryKind];
     }
 
     // The Refany type is the only struct type whose structure is implicitly assumed by IL.  We need its fields.
index ca9da82..6baf601 100644 (file)
@@ -4687,10 +4687,10 @@ inline void BasicBlock::InitVarSets(Compiler* comp)
     VarSetOps::AssignNoCopy(comp, bbLiveOut, VarSetOps::MakeEmpty(comp));
     VarSetOps::AssignNoCopy(comp, bbScope, VarSetOps::MakeEmpty(comp));
 
-    bbHeapUse     = false;
-    bbHeapDef     = false;
-    bbHeapLiveIn  = false;
-    bbHeapLiveOut = false;
+    bbMemoryUse     = emptyMemoryKindSet;
+    bbMemoryDef     = emptyMemoryKindSet;
+    bbMemoryLiveIn  = emptyMemoryKindSet;
+    bbMemoryLiveOut = emptyMemoryKindSet;
 }
 
 // Returns true if the basic block ends with GT_JMP
index e27d207..b22bf6d 100644 (file)
@@ -39,7 +39,7 @@ CompMemKindMacro(IndirAssignMap)
 CompMemKindMacro(FieldSeqStore)
 CompMemKindMacro(ZeroOffsetFieldMap)
 CompMemKindMacro(ArrayInfoMap)
-CompMemKindMacro(HeapPhiArg)
+CompMemKindMacro(MemoryPhiArg)
 CompMemKindMacro(CSE)
 CompMemKindMacro(GC)
 CompMemKindMacro(CorSig)
index 29c9508..269a5c2 100644 (file)
@@ -15127,14 +15127,17 @@ BasicBlock* Compiler::bbNewBasicBlock(BBjumpKinds jumpKind)
         VarSetOps::AssignNoCopy(this, block->bbScope, VarSetOps::UninitVal());
     }
 
-    block->bbHeapUse     = false;
-    block->bbHeapDef     = false;
-    block->bbHeapLiveIn  = false;
-    block->bbHeapLiveOut = false;
-
-    block->bbHeapSsaPhiFunc = nullptr;
-    block->bbHeapSsaNumIn   = 0;
-    block->bbHeapSsaNumOut  = 0;
+    block->bbMemoryUse     = emptyMemoryKindSet;
+    block->bbMemoryDef     = emptyMemoryKindSet;
+    block->bbMemoryLiveIn  = emptyMemoryKindSet;
+    block->bbMemoryLiveOut = emptyMemoryKindSet;
+
+    for (MemoryKind memoryKind : allMemoryKinds())
+    {
+        block->bbMemorySsaPhiFunc[memoryKind] = nullptr;
+        block->bbMemorySsaNumIn[memoryKind]   = 0;
+        block->bbMemorySsaNumOut[memoryKind]  = 0;
+    }
 
     // Make sure we reserve a NOT_IN_LOOP value that isn't a legal table index.
     static_assert_no_msg(MAX_LOOP_NUM < BasicBlock::NOT_IN_LOOP);
index 08e13b6..bf81bcd 100644 (file)
@@ -197,7 +197,7 @@ void Compiler::fgLocalVarLivenessInit()
 #ifndef LEGACY_BACKEND
 //------------------------------------------------------------------------
 // fgPerNodeLocalVarLiveness:
-//   Set fgCurHeapUse and fgCurHeapDef when the global heap is read or updated
+//   Set fgCurMemoryUse and fgCurMemoryDef when memory is read or updated
 //   Call fgMarkUseDef for any Local variables encountered
 //
 // Arguments:
@@ -225,38 +225,39 @@ void Compiler::fgPerNodeLocalVarLiveness(GenTree* tree)
             break;
 
         case GT_CLS_VAR:
-            // For Volatile indirection, first mutate the global heap
-            // see comments in ValueNum.cpp (under case GT_CLS_VAR)
-            // This models Volatile reads as def-then-use of the heap.
-            // and allows for a CSE of a subsequent non-volatile read
+            // For Volatile indirection, first mutate GcHeap.
+            // See comments in ValueNum.cpp (under case GT_CLS_VAR)
+            // This models Volatile reads as def-then-use of memory
+            // and allows for a CSE of a subsequent non-volatile read.
             if ((tree->gtFlags & GTF_FLD_VOLATILE) != 0)
             {
                 // For any Volatile indirection, we must handle it as a
-                // definition of the global heap
-                fgCurHeapDef = true;
+                // definition of GcHeap
+                fgCurMemoryDef |= memoryKindSet(GcHeap);
             }
-            // If the GT_CLS_VAR is the lhs of an assignment, we'll handle it as a heap def, when we get to assignment.
+            // If the GT_CLS_VAR is the lhs of an assignment, we'll handle it as a GcHeap def, when we get to
+            // assignment.
             // Otherwise, we treat it as a use here.
             if ((tree->gtFlags & GTF_CLS_VAR_ASG_LHS) == 0)
             {
-                fgCurHeapUse = true;
+                fgCurMemoryUse |= memoryKindSet(GcHeap);
             }
             break;
 
         case GT_IND:
-            // For Volatile indirection, first mutate the global heap
+            // For Volatile indirection, first mutate GcHeap
             // see comments in ValueNum.cpp (under case GT_CLS_VAR)
-            // This models Volatile reads as def-then-use of the heap.
+            // This models Volatile reads as def-then-use of memory.
             // and allows for a CSE of a subsequent non-volatile read
             if ((tree->gtFlags & GTF_IND_VOLATILE) != 0)
             {
                 // For any Volatile indirection, we must handle it as a
-                // definition of the global heap
-                fgCurHeapDef = true;
+                // definition of the GcHeap
+                fgCurMemoryDef |= memoryKindSet(GcHeap);
             }
 
             // If the GT_IND is the lhs of an assignment, we'll handle it
-            // as a heap def, when we get to assignment.
+            // as a memory def, when we get to assignment.
             // Otherwise, we treat it as a use here.
             if ((tree->gtFlags & GTF_IND_ASG_LHS) == 0)
             {
@@ -265,7 +266,7 @@ void Compiler::fgPerNodeLocalVarLiveness(GenTree* tree)
                 GenTreePtr           addrArg         = tree->gtOp.gtOp1->gtEffectiveVal(/*commaOnly*/ true);
                 if (!addrArg->DefinesLocalAddr(this, /*width doesn't matter*/ 0, &dummyLclVarTree, &dummyIsEntire))
                 {
-                    fgCurHeapUse = true;
+                    fgCurMemoryUse |= memoryKindSet(GcHeap);
                 }
                 else
                 {
@@ -282,22 +283,22 @@ void Compiler::fgPerNodeLocalVarLiveness(GenTree* tree)
             unreached();
             break;
 
-        // We'll assume these are use-then-defs of the heap.
+        // We'll assume these are use-then-defs of memory.
         case GT_LOCKADD:
         case GT_XADD:
         case GT_XCHG:
         case GT_CMPXCHG:
-            fgCurHeapUse   = true;
-            fgCurHeapDef   = true;
-            fgCurHeapHavoc = true;
+            fgCurMemoryUse |= memoryKindSet(GcHeap);
+            fgCurMemoryDef |= memoryKindSet(GcHeap);
+            fgCurMemoryHavoc |= memoryKindSet(GcHeap);
             break;
 
         case GT_MEMORYBARRIER:
-            // Simliar to any Volatile indirection, we must handle this as a definition of the global heap
-            fgCurHeapDef = true;
+            // Simliar to any Volatile indirection, we must handle this as a definition of GcHeap
+            fgCurMemoryDef |= memoryKindSet(GcHeap);
             break;
 
-        // For now, all calls read/write the heap, the latter in its entirety.  Might tighten this case later.
+        // For now, all calls read/write GcHeap, the latter in its entirety.  Might tighten this case later.
         case GT_CALL:
         {
             GenTreeCall* call    = tree->AsCall();
@@ -313,9 +314,9 @@ void Compiler::fgPerNodeLocalVarLiveness(GenTree* tree)
             }
             if (modHeap)
             {
-                fgCurHeapUse   = true;
-                fgCurHeapDef   = true;
-                fgCurHeapHavoc = true;
+                fgCurMemoryUse |= memoryKindSet(GcHeap);
+                fgCurMemoryDef |= memoryKindSet(GcHeap);
+                fgCurMemoryHavoc |= memoryKindSet(GcHeap);
             }
         }
 
@@ -351,14 +352,14 @@ void Compiler::fgPerNodeLocalVarLiveness(GenTree* tree)
 
         default:
 
-            // Determine whether it defines a heap location.
+            // Determine whether it defines a GcHeap location.
             if (tree->OperIsAssignment() || tree->OperIsBlkOp())
             {
                 GenTreeLclVarCommon* dummyLclVarTree = nullptr;
                 if (!tree->DefinesLocal(this, &dummyLclVarTree))
                 {
-                    // If it doesn't define a local, then it might update the heap.
-                    fgCurHeapDef = true;
+                    // If it doesn't define a local, then it might update the GC heap.
+                    fgCurMemoryDef |= memoryKindSet(GcHeap);
                 }
             }
             break;
@@ -409,10 +410,10 @@ void Compiler::fgPerBlockLocalVarLiveness()
             VarSetOps::Assign(this, block->bbVarDef, liveAll);
             VarSetOps::Assign(this, block->bbLiveIn, liveAll);
             VarSetOps::Assign(this, block->bbLiveOut, liveAll);
-            block->bbHeapUse     = true;
-            block->bbHeapDef     = true;
-            block->bbHeapLiveIn  = true;
-            block->bbHeapLiveOut = true;
+            block->bbMemoryUse     = fullMemoryKindSet;
+            block->bbMemoryDef     = fullMemoryKindSet;
+            block->bbMemoryLiveIn  = fullMemoryKindSet;
+            block->bbMemoryLiveOut = fullMemoryKindSet;
 
             switch (block->bbJumpKind)
             {
@@ -439,9 +440,9 @@ void Compiler::fgPerBlockLocalVarLiveness()
         VarSetOps::ClearD(this, fgCurUseSet);
         VarSetOps::ClearD(this, fgCurDefSet);
 
-        fgCurHeapUse   = false;
-        fgCurHeapDef   = false;
-        fgCurHeapHavoc = false;
+        fgCurMemoryUse   = emptyMemoryKindSet;
+        fgCurMemoryDef   = emptyMemoryKindSet;
+        fgCurMemoryHavoc = emptyMemoryKindSet;
 
         compCurBB = block;
         if (!block->IsLIR())
@@ -501,19 +502,25 @@ void Compiler::fgPerBlockLocalVarLiveness()
             printf("BB%02u", block->bbNum);
             printf(" USE(%d)=", VarSetOps::Count(this, fgCurUseSet));
             lvaDispVarSet(fgCurUseSet, allVars);
-            if (fgCurHeapUse)
+            for (MemoryKind memoryKind : allMemoryKinds())
             {
-                printf(" + HEAP");
+                if ((fgCurMemoryUse & memoryKindSet(memoryKind)) != 0)
+                {
+                    printf(" + %s", memoryKindNames[memoryKind]);
+                }
             }
             printf("\n     DEF(%d)=", VarSetOps::Count(this, fgCurDefSet));
             lvaDispVarSet(fgCurDefSet, allVars);
-            if (fgCurHeapDef)
+            for (MemoryKind memoryKind : allMemoryKinds())
             {
-                printf(" + HEAP");
-            }
-            if (fgCurHeapHavoc)
-            {
-                printf("*");
+                if ((fgCurMemoryDef & memoryKindSet(memoryKind)) != 0)
+                {
+                    printf(" + %s", memoryKindNames[memoryKind]);
+                }
+                if ((fgCurMemoryHavoc & memoryKindSet(memoryKind)) != 0)
+                {
+                    printf("*");
+                }
             }
             printf("\n\n");
         }
@@ -521,14 +528,14 @@ void Compiler::fgPerBlockLocalVarLiveness()
 
         VarSetOps::Assign(this, block->bbVarUse, fgCurUseSet);
         VarSetOps::Assign(this, block->bbVarDef, fgCurDefSet);
-        block->bbHeapUse   = fgCurHeapUse;
-        block->bbHeapDef   = fgCurHeapDef;
-        block->bbHeapHavoc = fgCurHeapHavoc;
+        block->bbMemoryUse   = fgCurMemoryUse;
+        block->bbMemoryDef   = fgCurMemoryDef;
+        block->bbMemoryHavoc = fgCurMemoryHavoc;
 
         /* also initialize the IN set, just in case we will do multiple DFAs */
 
         VarSetOps::AssignNoCopy(this, block->bbLiveIn, VarSetOps::MakeEmpty(this));
-        block->bbHeapLiveIn = false;
+        block->bbMemoryLiveIn = emptyMemoryKindSet;
     }
 }
 
@@ -1066,16 +1073,16 @@ class LiveVarAnalysis
 
     bool m_hasPossibleBackEdge;
 
-    bool      m_heapLiveIn;
-    bool      m_heapLiveOut;
+    unsigned  m_memoryLiveIn;
+    unsigned  m_memoryLiveOut;
     VARSET_TP m_liveIn;
     VARSET_TP m_liveOut;
 
     LiveVarAnalysis(Compiler* compiler)
         : m_compiler(compiler)
         , m_hasPossibleBackEdge(false)
-        , m_heapLiveIn(false)
-        , m_heapLiveOut(false)
+        , m_memoryLiveIn(emptyMemoryKindSet)
+        , m_memoryLiveOut(emptyMemoryKindSet)
         , m_liveIn(VarSetOps::MakeEmpty(compiler))
         , m_liveOut(VarSetOps::MakeEmpty(compiler))
     {
@@ -1085,7 +1092,7 @@ class LiveVarAnalysis
     {
         /* Compute the 'liveOut' set */
         VarSetOps::ClearD(m_compiler, m_liveOut);
-        m_heapLiveOut = false;
+        m_memoryLiveOut = emptyMemoryKindSet;
         if (block->endsWithJmpMethod(m_compiler))
         {
             // A JMP uses all the arguments, so mark them all
@@ -1108,7 +1115,7 @@ class LiveVarAnalysis
         {
             BasicBlock* succ = (*succs);
             VarSetOps::UnionD(m_compiler, m_liveOut, succ->bbLiveIn);
-            m_heapLiveOut = m_heapLiveOut || (*succs)->bbHeapLiveIn;
+            m_memoryLiveOut |= (*succs)->bbMemoryLiveIn;
             if (succ->bbNum <= block->bbNum)
             {
                 m_hasPossibleBackEdge = true;
@@ -1129,9 +1136,9 @@ class LiveVarAnalysis
         VarSetOps::DiffD(m_compiler, m_liveIn, block->bbVarDef);
         VarSetOps::UnionD(m_compiler, m_liveIn, block->bbVarUse);
 
-        // Even if block->bbHeapDef is set, we must assume that it doesn't kill heap liveness from m_heapLiveOut,
-        // since (without proof otherwise) the use and def may touch different heap memory at run-time.
-        m_heapLiveIn = m_heapLiveOut || block->bbHeapUse;
+        // Even if block->bbMemoryDef is set, we must assume that it doesn't kill memory liveness from m_memoryLiveOut,
+        // since (without proof otherwise) the use and def may touch different memory at run-time.
+        m_memoryLiveIn = m_memoryLiveOut | block->bbMemoryUse;
 
         /* Can exceptions from this block be handled (in this function)? */
 
@@ -1183,14 +1190,14 @@ class LiveVarAnalysis
             }
         }
 
-        const bool heapLiveInChanged = (block->bbHeapLiveIn == 1) != m_heapLiveIn;
-        if (heapLiveInChanged || (block->bbHeapLiveOut == 1) != m_heapLiveOut)
+        const bool memoryLiveInChanged = (block->bbMemoryLiveIn != m_memoryLiveIn);
+        if (memoryLiveInChanged || (block->bbMemoryLiveOut != m_memoryLiveOut))
         {
-            block->bbHeapLiveIn  = m_heapLiveIn;
-            block->bbHeapLiveOut = m_heapLiveOut;
+            block->bbMemoryLiveIn  = m_memoryLiveIn;
+            block->bbMemoryLiveOut = m_memoryLiveOut;
         }
 
-        return liveInChanged || heapLiveInChanged;
+        return liveInChanged || memoryLiveInChanged;
     }
 
     void Run(bool updateInternalOnly)
@@ -1209,8 +1216,8 @@ class LiveVarAnalysis
             VarSetOps::ClearD(m_compiler, m_liveIn);
             VarSetOps::ClearD(m_compiler, m_liveOut);
 
-            m_heapLiveIn  = false;
-            m_heapLiveOut = false;
+            m_memoryLiveIn  = emptyMemoryKindSet;
+            m_memoryLiveOut = emptyMemoryKindSet;
 
             for (BasicBlock* block = m_compiler->fgLastBB; block; block = block->bbPrev)
             {
@@ -2961,15 +2968,21 @@ void Compiler::fgDispBBLiveness(BasicBlock* block)
     printf("BB%02u", block->bbNum);
     printf(" IN (%d)=", VarSetOps::Count(this, block->bbLiveIn));
     lvaDispVarSet(block->bbLiveIn, allVars);
-    if (block->bbHeapLiveIn)
+    for (MemoryKind memoryKind : allMemoryKinds())
     {
-        printf(" + HEAP");
+        if ((block->bbMemoryLiveIn & memoryKindSet(memoryKind)) != 0)
+        {
+            printf(" + %s", memoryKindNames[memoryKind]);
+        }
     }
     printf("\n     OUT(%d)=", VarSetOps::Count(this, block->bbLiveOut));
     lvaDispVarSet(block->bbLiveOut, allVars);
-    if (block->bbHeapLiveOut)
+    for (MemoryKind memoryKind : allMemoryKinds())
     {
-        printf(" + HEAP");
+        if ((block->bbMemoryLiveOut & memoryKindSet(memoryKind)) != 0)
+        {
+            printf(" + %s", memoryKindNames[memoryKind]);
+        }
     }
     printf("\n\n");
 }
index e985d37..7fce274 100644 (file)
@@ -1193,7 +1193,10 @@ void Compiler::optRecordLoop(BasicBlock*   head,
     optLoopTable[loopInd].lpFlags = 0;
 
     // We haven't yet recorded any side effects.
-    optLoopTable[loopInd].lpLoopHasHeapHavoc       = false;
+    for (MemoryKind memoryKind : allMemoryKinds())
+    {
+        optLoopTable[loopInd].lpLoopHasMemoryHavoc[memoryKind] = false;
+    }
     optLoopTable[loopInd].lpFieldsModified         = nullptr;
     optLoopTable[loopInd].lpArrayElemTypesModified = nullptr;
 
@@ -6397,7 +6400,7 @@ bool Compiler::optVNIsLoopInvariant(ValueNum vn, unsigned lnum, VNToBoolMap* loo
                 res                  = !optLoopContains(lnum, ssaDef->m_defLoc.m_blk->bbNatLoopNum);
             }
         }
-        else if (funcApp.m_func == VNF_PhiHeapDef)
+        else if (funcApp.m_func == VNF_PhiMemoryDef)
         {
             BasicBlock* defnBlk = reinterpret_cast<BasicBlock*>(vnStore->ConstantValue<ssize_t>(funcApp.m_args[0]));
             res                 = !optLoopContains(lnum, defnBlk->bbNatLoopNum);
@@ -6837,7 +6840,8 @@ void Compiler::optComputeLoopSideEffectsOfBlock(BasicBlock* blk)
 
     AddVariableLivenessAllContainingLoops(mostNestedLoop, blk);
 
-    bool heapHavoc = false; // True ==> there's a call or a memory store that has arbitrary heap effects.
+    // MemoryKinds for which an in-loop call or store has arbitrary effects.
+    MemoryKindSet memoryHavoc = emptyMemoryKindSet;
 
     // Now iterate over the remaining statements, and their trees.
     for (GenTreePtr stmts = blk->FirstNonPhiDef(); (stmts != nullptr); stmts = stmts->gtNext)
@@ -6846,8 +6850,8 @@ void Compiler::optComputeLoopSideEffectsOfBlock(BasicBlock* blk)
         {
             genTreeOps oper = tree->OperGet();
 
-            // Even after we set heapHavoc we still may want to know if a loop contains calls
-            if (heapHavoc)
+            // Even after we set memoryHavoc we still may want to know if a loop contains calls
+            if (memoryHavoc == fullMemoryKindSet)
             {
                 if (oper == GT_CALL)
                 {
@@ -6858,18 +6862,18 @@ void Compiler::optComputeLoopSideEffectsOfBlock(BasicBlock* blk)
                 // If we just set lpContainsCall or it was previously set
                 if (optLoopTable[mostNestedLoop].lpContainsCall)
                 {
-                    // We can early exit after both heapHavoc and lpContainsCall are both set to true.
+                    // We can early exit after both memoryHavoc and lpContainsCall are both set to true.
                     break;
                 }
 
-                // We are just looking for GT_CALL nodes after heapHavoc was set.
+                // We are just looking for GT_CALL nodes after memoryHavoc was set.
                 continue;
             }
 
-            // otherwise heapHavoc is not set
-            assert(!heapHavoc);
+            // otherwise memoryHavoc is not set for at least one heap ID
+            assert(memoryHavoc != fullMemoryKindSet);
 
-            // This body is a distillation of the heap-side effect code of value numbering.
+            // This body is a distillation of the memory side-effect code of value numbering.
             // We also do a very limited analysis if byref PtrTo values, to cover some cases
             // that the compiler creates.
 
@@ -6884,7 +6888,7 @@ void Compiler::optComputeLoopSideEffectsOfBlock(BasicBlock* blk)
 
                     if ((tree->gtFlags & GTF_IND_VOLATILE) != 0)
                     {
-                        heapHavoc = true;
+                        memoryHavoc |= memoryKindSet(GcHeap);
                         continue;
                     }
 
@@ -6906,12 +6910,12 @@ void Compiler::optComputeLoopSideEffectsOfBlock(BasicBlock* blk)
                                 CORINFO_CLASS_HANDLE elemType =
                                     CORINFO_CLASS_HANDLE(vnStore->ConstantValue<size_t>(funcApp.m_args[0]));
                                 AddModifiedElemTypeAllContainingLoops(mostNestedLoop, elemType);
-                                // Don't set heapHavoc below.
+                                // Don't set memoryHavoc below.
                                 continue;
                             }
                         }
                         // Otherwise...
-                        heapHavoc = true;
+                        memoryHavoc |= memoryKindSet(GcHeap);
                     }
                     // Is the LHS an array index expression?
                     else if (lhs->ParseArrayElemForm(this, &arrInfo, &fldSeqArrElem))
@@ -6932,7 +6936,7 @@ void Compiler::optComputeLoopSideEffectsOfBlock(BasicBlock* blk)
                         if (arg->IsFieldAddr(this, &obj, &staticOffset, &fldSeq) &&
                             (fldSeq != FieldSeqStore::NotAField()))
                         {
-                            // Get the first (object) field from field seq.  Heap[field] will yield the "field map".
+                            // Get the first (object) field from field seq.  GcHeap[field] will yield the "field map".
                             assert(fldSeq != nullptr);
                             if (fldSeq->IsFirstElemFieldSeq())
                             {
@@ -6944,7 +6948,7 @@ void Compiler::optComputeLoopSideEffectsOfBlock(BasicBlock* blk)
                         }
                         else
                         {
-                            heapHavoc = true;
+                            memoryHavoc |= memoryKindSet(GcHeap);
                         }
                     }
                 }
@@ -6954,8 +6958,8 @@ void Compiler::optComputeLoopSideEffectsOfBlock(BasicBlock* blk)
                     bool                 isEntire;
                     if (!tree->DefinesLocal(this, &lclVarTree, &isEntire))
                     {
-                        // For now, assume arbitrary side effects on the heap...
-                        heapHavoc = true;
+                        // For now, assume arbitrary side effects on GcHeap...
+                        memoryHavoc |= memoryKindSet(GcHeap);
                     }
                 }
                 else if (lhs->OperGet() == GT_CLS_VAR)
@@ -7019,7 +7023,7 @@ void Compiler::optComputeLoopSideEffectsOfBlock(BasicBlock* blk)
                     case GT_XCHG:    // Binop
                     case GT_CMPXCHG: // Specialop
                     {
-                        heapHavoc = true;
+                        memoryHavoc |= memoryKindSet(GcHeap);
                     }
                     break;
 
@@ -7035,7 +7039,7 @@ void Compiler::optComputeLoopSideEffectsOfBlock(BasicBlock* blk)
                             CorInfoHelpFunc helpFunc = eeGetHelperNum(call->gtCallMethHnd);
                             if (s_helperCallProperties.MutatesHeap(helpFunc))
                             {
-                                heapHavoc = true;
+                                memoryHavoc |= memoryKindSet(GcHeap);
                             }
                             else if (s_helperCallProperties.MayRunCctor(helpFunc))
                             {
@@ -7045,33 +7049,39 @@ void Compiler::optComputeLoopSideEffectsOfBlock(BasicBlock* blk)
                                 // and might have arbitrary side effects.
                                 if ((tree->gtFlags & GTF_CALL_HOISTABLE) == 0)
                                 {
-                                    heapHavoc = true;
+                                    memoryHavoc |= memoryKindSet(GcHeap);
                                 }
                             }
                         }
                         else
                         {
-                            heapHavoc = true;
+                            memoryHavoc |= memoryKindSet(GcHeap);
                         }
                         break;
                     }
 
                     default:
-                        // All other gtOper node kinds, leave 'heapHavoc' unchanged (i.e. false)
+                        // All other gtOper node kinds, leave 'memoryHavoc' unchanged (i.e. false)
                         break;
                 }
             }
         }
     }
 
-    if (heapHavoc)
+    if (memoryHavoc != emptyMemoryKindSet)
     {
-        // Record that all loops containing this block have heap havoc effects.
+        // Record that all loops containing this block have memory havoc effects.
         unsigned lnum = mostNestedLoop;
         while (lnum != BasicBlock::NOT_IN_LOOP)
         {
-            optLoopTable[lnum].lpLoopHasHeapHavoc = true;
-            lnum                                  = optLoopTable[lnum].lpParent;
+            for (MemoryKind memoryKind : allMemoryKinds())
+            {
+                if ((memoryHavoc & memoryKindSet(memoryKind)) != 0)
+                {
+                    optLoopTable[lnum].lpLoopHasMemoryHavoc[memoryKind] = true;
+                }
+            }
+            lnum = optLoopTable[lnum].lpParent;
         }
     }
 }
index 5d553c1..bc7f8f2 100644 (file)
@@ -103,12 +103,19 @@ void Compiler::fgResetForSsa()
     {
         lvaTable[i].lvPerSsaData.Reset();
     }
-    lvHeapPerSsaData.Reset();
-    m_heapSsaMap = nullptr;
+    lvMemoryPerSsaData.Reset();
+    for (MemoryKind memoryKind : allMemoryKinds())
+    {
+        m_memorySsaMap[memoryKind] = nullptr;
+    }
+
     for (BasicBlock* blk = fgFirstBB; blk != nullptr; blk = blk->bbNext)
     {
         // Eliminate phis.
-        blk->bbHeapSsaPhiFunc = nullptr;
+        for (MemoryKind memoryKind : allMemoryKinds())
+        {
+            blk->bbMemorySsaPhiFunc[memoryKind] = nullptr;
+        }
         if (blk->bbTreeList != nullptr)
         {
             GenTreePtr last = blk->bbTreeList->gtPrev;
@@ -804,29 +811,34 @@ void SsaBuilder::InsertPhiFunctions(BasicBlock** postOrder, int count)
             }
         }
 
-        // Now make a similar phi definition if the block defines Heap.
-        if (block->bbHeapDef)
+        // Now make a similar phi definition if the block defines memory.
+        if (block->bbMemoryDef != 0)
         {
             // For each block "bbInDomFront" that is in the dominance frontier of "block".
             for (BlkSet::KeyIterator iterBlk = blkIdf->Begin(); !iterBlk.Equal(blkIdf->End()); ++iterBlk)
             {
                 BasicBlock* bbInDomFront = iterBlk.Get();
-                DBG_SSA_JITDUMP("     Considering BB%02u in dom frontier of BB%02u for Heap phis:\n",
+                DBG_SSA_JITDUMP("     Considering BB%02u in dom frontier of BB%02u for Memory phis:\n",
                                 bbInDomFront->bbNum, block->bbNum);
 
-                // Check if Heap is live into block "*iterBlk".
-                if (!bbInDomFront->bbHeapLiveIn)
+                for (MemoryKind memoryKind : allMemoryKinds())
                 {
-                    continue;
-                }
+                    // Check if memoryKind is live into block "*iterBlk".
+                    if ((bbInDomFront->bbMemoryLiveIn & memoryKindSet(memoryKind)) == 0)
+                    {
+                        continue;
+                    }
 
-                // Check if we've already inserted a phi node.
-                if (bbInDomFront->bbHeapSsaPhiFunc == nullptr)
-                {
-                    // We have a variable i that is defined in block j and live at l, and l belongs to dom frontier of
-                    // j. So insert a phi node at l.
-                    JITDUMP("Inserting phi definition for Heap at start of BB%02u.\n", bbInDomFront->bbNum);
-                    bbInDomFront->bbHeapSsaPhiFunc = BasicBlock::EmptyHeapPhiDef;
+                    // Check if we've already inserted a phi node.
+                    if (bbInDomFront->bbMemorySsaPhiFunc[memoryKind] == nullptr)
+                    {
+                        // We have a variable i that is defined in block j and live at l, and l belongs to dom frontier
+                        // of
+                        // j. So insert a phi node at l.
+                        JITDUMP("Inserting phi definition for %s at start of BB%02u.\n", memoryKindNames[memoryKind],
+                                bbInDomFront->bbNum);
+                        bbInDomFront->bbMemorySsaPhiFunc[memoryKind] = BasicBlock::EmptyMemoryPhiDef;
+                    }
                 }
             }
         }
@@ -944,8 +956,8 @@ void SsaBuilder::TreeRenameVariables(GenTree* tree, BasicBlock* block, SsaRename
         }
     }
 
-    // Figure out if "tree" may make a new heap state (if we care for this block).
-    if (!block->bbHeapHavoc)
+    // Figure out if "tree" may make a new GC heap state (if we care for this block).
+    if ((block->bbMemoryHavoc & memoryKindSet(GcHeap)) == 0)
     {
         if (tree->OperIsAssignment() || tree->OperIsBlkOp())
         {
@@ -954,21 +966,21 @@ void SsaBuilder::TreeRenameVariables(GenTree* tree, BasicBlock* block, SsaRename
                 GenTreeLclVarCommon* lclVarNode;
                 if (!tree->DefinesLocal(m_pCompiler, &lclVarNode))
                 {
-                    // It *may* define the heap in a non-havoc way.  Make a new SSA # -- associate with this node.
-                    unsigned count = pRenameState->CountForHeapDef();
-                    pRenameState->PushHeap(block, count);
-                    m_pCompiler->GetHeapSsaMap()->Set(tree, count);
+                    // It *may* define the GC heap in a non-havoc way.  Make a new SSA # -- associate with this node.
+                    unsigned count = pRenameState->CountForMemoryDef();
+                    pRenameState->PushMemory(GcHeap, block, count);
+                    m_pCompiler->GetMemorySsaMap(GcHeap)->Set(tree, count);
 #ifdef DEBUG
                     if (JitTls::GetCompiler()->verboseSsa)
                     {
                         printf("Node ");
                         Compiler::printTreeID(tree);
-                        printf(" (in try block) may define heap; ssa # = %d.\n", count);
+                        printf(" (in try block) may define GC heap; ssa # = %d.\n", count);
                     }
 #endif // DEBUG
 
                     // Now add this SSA # to all phis of the reachable catch blocks.
-                    AddHeapDefToHandlerPhis(block, count);
+                    AddMemoryDefToHandlerPhis(GcHeap, block, count);
                 }
             }
         }
@@ -1154,7 +1166,7 @@ void SsaBuilder::AddDefToHandlerPhis(BasicBlock* block, unsigned lclNum, unsigne
     }
 }
 
-void SsaBuilder::AddHeapDefToHandlerPhis(BasicBlock* block, unsigned count)
+void SsaBuilder::AddMemoryDefToHandlerPhis(MemoryKind memoryKind, BasicBlock* block, unsigned count)
 {
     if (m_pCompiler->ehBlockHasExnFlowDsc(block))
     {
@@ -1165,39 +1177,39 @@ void SsaBuilder::AddHeapDefToHandlerPhis(BasicBlock* block, unsigned count)
         }
 
         // Otherwise...
-        DBG_SSA_JITDUMP("Definition of Heap/d:%d in block BB%02u has exn handler; adding as phi arg to handlers.\n",
-                        count, block->bbNum);
+        DBG_SSA_JITDUMP("Definition of %s/d:%d in block BB%02u has exn handler; adding as phi arg to handlers.\n",
+                        memoryKindNames[memoryKind], count, block->bbNum);
         EHblkDsc* tryBlk = m_pCompiler->ehGetBlockExnFlowDsc(block);
         while (true)
         {
             BasicBlock* handler = tryBlk->ExFlowBlock();
 
-            // Is Heap live on entry to the handler?
-            if (handler->bbHeapLiveIn)
+            // Is memoryKind live on entry to the handler?
+            if ((handler->bbMemoryLiveIn & memoryKindSet(memoryKind)) != 0)
             {
-                assert(handler->bbHeapSsaPhiFunc != nullptr);
+                assert(handler->bbMemorySsaPhiFunc != nullptr);
 
-                // Add "count" to the phi args of Heap.
-                if (handler->bbHeapSsaPhiFunc == BasicBlock::EmptyHeapPhiDef)
+                // Add "count" to the phi args of memoryKind.
+                BasicBlock::MemoryPhiArg*& handlerMemoryPhi = handler->bbMemorySsaPhiFunc[memoryKind];
+                if (handlerMemoryPhi == BasicBlock::EmptyMemoryPhiDef)
                 {
-                    handler->bbHeapSsaPhiFunc = new (m_pCompiler) BasicBlock::HeapPhiArg(count);
+                    handlerMemoryPhi = new (m_pCompiler) BasicBlock::MemoryPhiArg(count);
                 }
                 else
                 {
 #ifdef DEBUG
-                    BasicBlock::HeapPhiArg* curArg = handler->bbHeapSsaPhiFunc;
+                    BasicBlock::MemoryPhiArg* curArg = handler->bbMemorySsaPhiFunc[memoryKind];
                     while (curArg != nullptr)
                     {
                         assert(curArg->GetSsaNum() != count);
                         curArg = curArg->m_nextArg;
                     }
 #endif // DEBUG
-                    handler->bbHeapSsaPhiFunc =
-                        new (m_pCompiler) BasicBlock::HeapPhiArg(count, handler->bbHeapSsaPhiFunc);
+                    handlerMemoryPhi = new (m_pCompiler) BasicBlock::MemoryPhiArg(count, handlerMemoryPhi);
                 }
 
-                DBG_SSA_JITDUMP("   Added phi arg u:%d for Heap to phi defn in handler block BB%02u.\n", count,
-                                handler->bbNum);
+                DBG_SSA_JITDUMP("   Added phi arg u:%d for %s to phi defn in handler block BB%02u.\n", count,
+                                memoryKindNames[memoryKind], memoryKind, handler->bbNum);
             }
             unsigned tryInd = tryBlk->ebdEnclosingTryIndex;
             if (tryInd == EHblkDsc::NO_ENCLOSING_INDEX)
@@ -1221,19 +1233,22 @@ void SsaBuilder::BlockRenameVariables(BasicBlock* block, SsaRenameState* pRename
 {
     // Walk the statements of the block and rename the tree variables.
 
-    // First handle the incoming Heap state.
-
-    // Is there an Phi definition for heap at the start of this block?
-    if (block->bbHeapSsaPhiFunc != nullptr)
+    // First handle the incoming memory states.
+    for (MemoryKind memoryKind : allMemoryKinds())
     {
-        unsigned count = pRenameState->CountForHeapDef();
-        pRenameState->PushHeap(block, count);
+        // Is there an Phi definition for memoryKind at the start of this block?
+        if (block->bbMemorySsaPhiFunc[memoryKind] != nullptr)
+        {
+            unsigned count = pRenameState->CountForMemoryDef();
+            pRenameState->PushMemory(memoryKind, block, count);
 
-        DBG_SSA_JITDUMP("Ssa # for Heap phi on entry to BB%02u is %d.\n", block->bbNum, count);
-    }
+            DBG_SSA_JITDUMP("Ssa # for %s phi on entry to BB%02u is %d.\n", memoryKindNames[memoryKind], block->bbNum,
+                            count);
+        }
 
-    // Record the "in" Ssa # for Heap.
-    block->bbHeapSsaNumIn = pRenameState->CountForHeapUse();
+        // Record the "in" Ssa # for memoryKind.
+        block->bbMemorySsaNumIn[memoryKind] = pRenameState->CountForMemoryUse(memoryKind);
+    }
 
     // We need to iterate over phi definitions, to give them SSA names, but we need
     // to know which are which, so we don't add phi definitions to handler phi arg lists.
@@ -1253,22 +1268,26 @@ void SsaBuilder::BlockRenameVariables(BasicBlock* block, SsaRenameState* pRename
         }
     }
 
-    // Now handle the final heap state.
-
-    // If the block defines Heap, allocate an SSA variable for the final heap state in the block.
-    // (This may be redundant with the last SSA var explicitly created, but there's no harm in that.)
-    if (block->bbHeapDef)
+    // Now handle the final memory states.
+    for (MemoryKind memoryKind : allMemoryKinds())
     {
-        unsigned count = pRenameState->CountForHeapDef();
-        pRenameState->PushHeap(block, count);
-        AddHeapDefToHandlerPhis(block, count);
-    }
+        MemoryKindSet memorySet = memoryKindSet(memoryKind);
+
+        // If the block defines memory, allocate an SSA variable for the final memory state in the block.
+        // (This may be redundant with the last SSA var explicitly created, but there's no harm in that.)
+        if ((block->bbMemoryDef & memorySet) != 0)
+        {
+            unsigned count = pRenameState->CountForMemoryDef();
+            pRenameState->PushMemory(memoryKind, block, count);
+            AddMemoryDefToHandlerPhis(memoryKind, block, count);
+        }
 
-    // Record the "out" Ssa" # for Heap.
-    block->bbHeapSsaNumOut = pRenameState->CountForHeapUse();
+        // Record the "out" Ssa" # for memoryKind.
+        block->bbMemorySsaNumOut[memoryKind] = pRenameState->CountForMemoryUse(memoryKind);
 
-    DBG_SSA_JITDUMP("Ssa # for Heap on entry to BB%02u is %d; on exit is %d.\n", block->bbNum, block->bbHeapSsaNumIn,
-                    block->bbHeapSsaNumOut);
+        DBG_SSA_JITDUMP("Ssa # for %s on entry to BB%02u is %d; on exit is %d.\n", memoryKindNames[memoryKind],
+                        block->bbNum, block->bbMemorySsaNumIn[memoryKind], block->bbMemorySsaNumOut[memoryKind]);
+    }
 }
 
 /**
@@ -1328,35 +1347,40 @@ void SsaBuilder::AssignPhiNodeRhsVariables(BasicBlock* block, SsaRenameState* pR
             m_pCompiler->fgSetStmtSeq(stmt);
         }
 
-        // Now handle Heap.
-        if (succ->bbHeapSsaPhiFunc != nullptr)
+        // Now handle memory.
+        for (MemoryKind memoryKind : allMemoryKinds())
         {
-            if (succ->bbHeapSsaPhiFunc == BasicBlock::EmptyHeapPhiDef)
+            BasicBlock::MemoryPhiArg*& succMemoryPhi = succ->bbMemorySsaPhiFunc[memoryKind];
+            if (succMemoryPhi != nullptr)
             {
-                succ->bbHeapSsaPhiFunc = new (m_pCompiler) BasicBlock::HeapPhiArg(block->bbHeapSsaNumOut);
-            }
-            else
-            {
-                BasicBlock::HeapPhiArg* curArg = succ->bbHeapSsaPhiFunc;
-                unsigned                ssaNum = block->bbHeapSsaNumOut;
-                bool                    found  = false;
-                // This is a quadratic algorithm.  We might need to consider some switch over to a hash table
-                // representation for the arguments of a phi node, to make this linear.
-                while (curArg != nullptr)
+                if (succMemoryPhi == BasicBlock::EmptyMemoryPhiDef)
                 {
-                    if (curArg->m_ssaNum == ssaNum)
-                    {
-                        found = true;
-                        break;
-                    }
-                    curArg = curArg->m_nextArg;
+                    succMemoryPhi = new (m_pCompiler) BasicBlock::MemoryPhiArg(block->bbMemorySsaNumOut[memoryKind]);
                 }
-                if (!found)
+                else
                 {
-                    succ->bbHeapSsaPhiFunc = new (m_pCompiler) BasicBlock::HeapPhiArg(ssaNum, succ->bbHeapSsaPhiFunc);
+                    BasicBlock::MemoryPhiArg* curArg = succMemoryPhi;
+                    unsigned                  ssaNum = block->bbMemorySsaNumOut[memoryKind];
+                    bool                      found  = false;
+                    // This is a quadratic algorithm.  We might need to consider some switch over to a hash table
+                    // representation for the arguments of a phi node, to make this linear.
+                    while (curArg != nullptr)
+                    {
+                        if (curArg->m_ssaNum == ssaNum)
+                        {
+                            found = true;
+                            break;
+                        }
+                        curArg = curArg->m_nextArg;
+                    }
+                    if (!found)
+                    {
+                        succMemoryPhi = new (m_pCompiler) BasicBlock::MemoryPhiArg(ssaNum, succMemoryPhi);
+                    }
                 }
+                DBG_SSA_JITDUMP("  Added phi arg for %s u:%d from BB%02u in BB%02u.\n", memoryKindNames[memoryKind],
+                                block->bbMemorySsaNumOut[memoryKind], block->bbNum, succ->bbNum);
             }
-            DBG_SSA_JITDUMP("  Added phi arg for Heap from BB%02u in BB%02u.\n", block->bbNum, succ->bbNum);
         }
 
         // If "succ" is the first block of a try block (and "block" is not also in that try block)
@@ -1462,26 +1486,31 @@ void SsaBuilder::AssignPhiNodeRhsVariables(BasicBlock* block, SsaRenameState* pR
                     }
                 }
 
-                // Now handle Heap.
-                if (handlerStart->bbHeapSsaPhiFunc != nullptr)
+                // Now handle memory.
+                for (MemoryKind memoryKind : allMemoryKinds())
                 {
-                    if (handlerStart->bbHeapSsaPhiFunc == BasicBlock::EmptyHeapPhiDef)
+                    BasicBlock::MemoryPhiArg*& handlerMemoryPhi = handlerStart->bbMemorySsaPhiFunc[memoryKind];
+                    if (handlerMemoryPhi != nullptr)
                     {
-                        handlerStart->bbHeapSsaPhiFunc =
-                            new (m_pCompiler) BasicBlock::HeapPhiArg(block->bbHeapSsaNumOut);
-                    }
-                    else
-                    {
-                        // This path has a potential to introduce redundant phi args, due to multiple
-                        // preds of the same try-begin block having the same live-out heap def, and/or
-                        // due to nested try-begins each having preds with the same live-out heap def.
-                        // Avoid doing quadratic processing on handler phis, and instead live with the
-                        // occasional redundancy.
-                        handlerStart->bbHeapSsaPhiFunc = new (m_pCompiler)
-                            BasicBlock::HeapPhiArg(block->bbHeapSsaNumOut, handlerStart->bbHeapSsaPhiFunc);
+                        if (handlerMemoryPhi == BasicBlock::EmptyMemoryPhiDef)
+                        {
+                            handlerMemoryPhi =
+                                new (m_pCompiler) BasicBlock::MemoryPhiArg(block->bbMemorySsaNumOut[memoryKind]);
+                        }
+                        else
+                        {
+                            // This path has a potential to introduce redundant phi args, due to multiple
+                            // preds of the same try-begin block having the same live-out memory def, and/or
+                            // due to nested try-begins each having preds with the same live-out memory def.
+                            // Avoid doing quadratic processing on handler phis, and instead live with the
+                            // occasional redundancy.
+                            handlerMemoryPhi = new (m_pCompiler)
+                                BasicBlock::MemoryPhiArg(block->bbMemorySsaNumOut[memoryKind], handlerMemoryPhi);
+                        }
+                        DBG_SSA_JITDUMP("  Added phi arg for %s u:%d from BB%02u in BB%02u.\n",
+                                        memoryKindNames[memoryKind], block->bbMemorySsaNumOut[memoryKind], block->bbNum,
+                                        handlerStart->bbNum);
                     }
-                    DBG_SSA_JITDUMP("  Added phi arg for Heap from BB%02u in BB%02u.\n", block->bbNum,
-                                    handlerStart->bbNum);
                 }
 
                 tryInd = succTry->ebdEnclosingTryIndex;
@@ -1502,8 +1531,11 @@ void SsaBuilder::BlockPopStacks(BasicBlock* block, SsaRenameState* pRenameState)
     // Pop the names given to the non-phi nodes.
     pRenameState->PopBlockStacks(block);
 
-    // And for Heap.
-    pRenameState->PopBlockHeapStack(block);
+    // And for memory.
+    for (MemoryKind memoryKind : allMemoryKinds())
+    {
+        pRenameState->PopBlockMemoryStack(memoryKind, block);
+    }
 }
 
 /**
@@ -1552,20 +1584,27 @@ void SsaBuilder::RenameVariables(BlkToBlkSetMap* domTree, SsaRenameState* pRenam
             pRenameState->Push(nullptr, i, count);
         }
     }
-    // In ValueNum we'd assume un-inited heap gets FIRST_SSA_NUM.
-    // The heap is a parameter.  Use FIRST_SSA_NUM as first SSA name.
-    unsigned initHeapCount = pRenameState->CountForHeapDef();
-    assert(initHeapCount == SsaConfig::FIRST_SSA_NUM);
-    pRenameState->PushHeap(m_pCompiler->fgFirstBB, initHeapCount);
-
-    // Initialize the heap ssa numbers for unreachable blocks. ValueNum expects
-    // heap ssa numbers to have some intitial value.
+
+    // In ValueNum we'd assume un-inited memory gets FIRST_SSA_NUM.
+    // The memory is a parameter.  Use FIRST_SSA_NUM as first SSA name.
+    unsigned initMemoryCount = pRenameState->CountForMemoryDef();
+    assert(initMemoryCount == SsaConfig::FIRST_SSA_NUM);
+    for (MemoryKind memoryKind : allMemoryKinds())
+    {
+        pRenameState->PushMemory(memoryKind, m_pCompiler->fgFirstBB, initMemoryCount);
+    }
+
+    // Initialize the memory ssa numbers for unreachable blocks. ValueNum expects
+    // memory ssa numbers to have some intitial value.
     for (BasicBlock* block = m_pCompiler->fgFirstBB; block; block = block->bbNext)
     {
         if (block->bbIDom == nullptr)
         {
-            block->bbHeapSsaNumIn  = initHeapCount;
-            block->bbHeapSsaNumOut = initHeapCount;
+            for (MemoryKind memoryKind : allMemoryKinds())
+            {
+                block->bbMemorySsaNumIn[memoryKind]  = initMemoryCount;
+                block->bbMemorySsaNumOut[memoryKind] = initMemoryCount;
+            }
         }
     }
 
@@ -1624,8 +1663,8 @@ void SsaBuilder::RenameVariables(BlkToBlkSetMap* domTree, SsaRenameState* pRenam
         }
     }
 
-    // Remember the number of Heap SSA names.
-    m_pCompiler->lvHeapNumSsaNames = pRenameState->HeapCount();
+    // Remember the number of memory SSA names.
+    m_pCompiler->lvMemoryNumSsaNames = pRenameState->MemoryCount();
 }
 
 #ifdef DEBUG
index 2fff065..e82a400 100644 (file)
@@ -164,8 +164,8 @@ private:
     // block of those handlers.
     void AddDefToHandlerPhis(BasicBlock* block, unsigned lclNum, unsigned count);
 
-    // Same as above, for "Heap".
-    void AddHeapDefToHandlerPhis(BasicBlock* block, unsigned count);
+    // Same as above, for memory.
+    void AddMemoryDefToHandlerPhis(MemoryKind memoryKind, BasicBlock* block, unsigned count);
 
     // Requires "block" to be non-NULL.  Requires "pRenameState" to be non-NULL and be currently used
     // for variables renaming. Assigns the rhs arguments to the phi, i.e., block's phi node arguments.
index a1e05f1..07d344d 100644 (file)
@@ -32,8 +32,8 @@ SsaRenameState::SsaRenameState(const jitstd::allocator<int>& alloc, unsigned lva
     : counts(nullptr)
     , stacks(nullptr)
     , definedLocs(alloc)
-    , heapStack(alloc)
-    , heapCount(0)
+    , memoryStack(alloc)
+    , memoryCount(0)
     , lvaCount(lvaCount)
     , m_alloc(alloc)
 {
@@ -200,11 +200,12 @@ void SsaRenameState::PopBlockStacks(BasicBlock* block)
 #endif // DEBUG
 }
 
-void SsaRenameState::PopBlockHeapStack(BasicBlock* block)
+void SsaRenameState::PopBlockMemoryStack(MemoryKind memoryKind, BasicBlock* block)
 {
-    while (heapStack.size() > 0 && heapStack.back().m_bb == block)
+    auto& stack = memoryStack[memoryKind];
+    while (stack.size() > 0 && stack.back().m_bb == block)
     {
-        heapStack.pop_back();
+        stack.pop_back();
     }
 }
 
index 1db36c5..02a9d14 100644 (file)
@@ -23,6 +23,53 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
 
 #include "jitstd.h"
 
+// Fixed-size array that can hold elements with no default constructor;
+// it will construct them all by forwarding whatever arguments are
+// supplied to its constructor.
+template <typename T, int N>
+class ConstructedArray
+{
+    union {
+        // Storage that gets used to hold the T objects.
+        unsigned char bytes[N * sizeof(T)];
+
+#if defined(_MSC_VER) && (_MSC_VER < 1900)
+        // With MSVC pre-VS2015, the code in the #else branch would hit error C2621,
+        // so in that case just count on pointer alignment being sufficient
+        // (currently T is only ever instantiated as jitstd::list<SsaRenameStateForBlock>)
+
+        // Unused (except to impart alignment requirement)
+        void* pointer;
+#else
+        // Unused (except to impart alignment requirement)
+        T alignedArray[N];
+#endif // defined(_MSC_VER) && (_MSC_VER < 1900)
+    };
+
+public:
+    T& operator[](size_t i)
+    {
+        return *(reinterpret_cast<T*>(bytes + i * sizeof(T)));
+    }
+
+    template <typename... Args>
+    ConstructedArray(Args&&... args)
+    {
+        for (int i = 0; i < N; ++i)
+        {
+            new (bytes + i * sizeof(T), jitstd::placement_t()) T(jitstd::forward<Args>(args)...);
+        }
+    }
+
+    ~ConstructedArray()
+    {
+        for (int i = 0; i < N; ++i)
+        {
+            operator[](i).~T();
+        }
+    }
+};
+
 struct SsaRenameStateForBlock
 {
     BasicBlock* m_bb;
@@ -74,32 +121,32 @@ struct SsaRenameState
     // Pop all stacks that have an entry for "bb" on top.
     void PopBlockStacks(BasicBlock* bb);
 
-    // Similar functions for the special implicit "Heap" variable.
-    unsigned CountForHeapDef()
+    // Similar functions for the special implicit memory variable.
+    unsigned CountForMemoryDef()
     {
-        if (heapCount == 0)
+        if (memoryCount == 0)
         {
-            heapCount = SsaConfig::FIRST_SSA_NUM;
+            memoryCount = SsaConfig::FIRST_SSA_NUM;
         }
-        unsigned res = heapCount;
-        heapCount++;
+        unsigned res = memoryCount;
+        memoryCount++;
         return res;
     }
-    unsigned CountForHeapUse()
+    unsigned CountForMemoryUse(MemoryKind memoryKind)
     {
-        return heapStack.back().m_count;
+        return memoryStack[memoryKind].back().m_count;
     }
 
-    void PushHeap(BasicBlock* bb, unsigned count)
+    void PushMemory(MemoryKind memoryKind, BasicBlock* bb, unsigned count)
     {
-        heapStack.push_back(SsaRenameStateForBlock(bb, count));
+        memoryStack[memoryKind].push_back(SsaRenameStateForBlock(bb, count));
     }
 
-    void PopBlockHeapStack(BasicBlock* bb);
+    void PopBlockMemoryStack(MemoryKind memoryKind, BasicBlock* bb);
 
-    unsigned HeapCount()
+    unsigned MemoryCount()
     {
-        return heapCount;
+        return memoryCount;
     }
 
 #ifdef DEBUG
@@ -117,9 +164,9 @@ private:
     // This list represents the set of locals defined in the current block.
     DefStack definedLocs;
 
-    // Same state for the special implicit Heap variable.
-    Stack    heapStack;
-    unsigned heapCount;
+    // Same state for the special implicit memory variables.
+    ConstructedArray<Stack, MemoryKindCount> memoryStack;
+    unsigned memoryCount;
 
     // Number of stacks/counts to allocate.
     unsigned lvaCount;
index 651b45b..37a743d 100644 (file)
@@ -1373,10 +1373,10 @@ TailCall:
                     goto TailCall;
                 }
             }
-            else if (funcApp.m_func == VNF_PhiDef || funcApp.m_func == VNF_PhiHeapDef)
+            else if (funcApp.m_func == VNF_PhiDef || funcApp.m_func == VNF_PhiMemoryDef)
             {
-                unsigned  lclNum = BAD_VAR_NUM;
-                bool      isHeap = false;
+                unsigned  lclNum   = BAD_VAR_NUM;
+                bool      isMemory = false;
                 VNFuncApp phiFuncApp;
                 bool      defArgIsFunc = false;
                 if (funcApp.m_func == VNF_PhiDef)
@@ -1386,8 +1386,8 @@ TailCall:
                 }
                 else
                 {
-                    assert(funcApp.m_func == VNF_PhiHeapDef);
-                    isHeap       = true;
+                    assert(funcApp.m_func == VNF_PhiMemoryDef);
+                    isMemory     = true;
                     defArgIsFunc = GetVNFunc(funcApp.m_args[1], &phiFuncApp);
                 }
                 if (defArgIsFunc && phiFuncApp.m_func == VNF_Phi)
@@ -1401,9 +1401,9 @@ TailCall:
                     assert(IsVNConstant(phiFuncApp.m_args[0]));
                     unsigned phiArgSsaNum = ConstantValue<unsigned>(phiFuncApp.m_args[0]);
                     ValueNum phiArgVN;
-                    if (isHeap)
+                    if (isMemory)
                     {
-                        phiArgVN = m_pComp->GetHeapPerSsaData(phiArgSsaNum)->m_vnPair.Get(vnk);
+                        phiArgVN = m_pComp->GetMemoryPerSsaData(phiArgSsaNum)->m_vnPair.Get(vnk);
                     }
                     else
                     {
@@ -1430,9 +1430,9 @@ TailCall:
                             }
                             assert(IsVNConstant(cur));
                             phiArgSsaNum = ConstantValue<unsigned>(cur);
-                            if (isHeap)
+                            if (isMemory)
                             {
-                                phiArgVN = m_pComp->GetHeapPerSsaData(phiArgSsaNum)->m_vnPair.Get(vnk);
+                                phiArgVN = m_pComp->GetMemoryPerSsaData(phiArgSsaNum)->m_vnPair.Get(vnk);
                             }
                             else
                             {
@@ -2465,9 +2465,10 @@ ValueNum ValueNumStore::VNApplySelectorsAssignTypeCoerce(ValueNum elem, var_type
 
 //------------------------------------------------------------------------
 // VNApplySelectorsAssign: Compute the value number corresponding to "map" but with
-//    the element at "fieldSeq" updated to have type "elem"; this is the new heap
-//    value for an assignment of value "elem" into the heap at location "fieldSeq"
-//    that occurs in block "block" and has type "indType".
+//    the element at "fieldSeq" updated to have type "elem"; this is the new memory
+//    value for an assignment of value "elem" into the memory at location "fieldSeq"
+//    that occurs in block "block" and has type "indType" (so long as the selectors
+//    into that memory occupy disjoint locations, which is true for GcHeap).
 //
 // Arguments:
 //    vnk - Identifies whether to recurse to Conservative or Liberal value numbers
@@ -2478,7 +2479,7 @@ ValueNum ValueNumStore::VNApplySelectorsAssignTypeCoerce(ValueNum elem, var_type
 //    block - Block where the assignment occurs
 //
 // Return Value:
-//    The value number corresopnding to the heap after the assignment.
+//    The value number corresponding to memory after the assignment.
 
 ValueNum ValueNumStore::VNApplySelectorsAssign(
     ValueNumKind vnk, ValueNum map, FieldSeqNode* fieldSeq, ValueNum elem, var_types indType, BasicBlock* block)
@@ -2722,7 +2723,7 @@ ValueNum Compiler::fgValueNumberArrIndexAssign(CORINFO_CLASS_HANDLE elemTypeEq,
     bool      invalidateArray      = false;
     ValueNum  elemTypeEqVN         = vnStore->VNForHandle(ssize_t(elemTypeEq), GTF_ICON_CLASS_HDL);
     var_types arrElemType          = DecodeElemType(elemTypeEq);
-    ValueNum  hAtArrType           = vnStore->VNForMapSelect(VNK_Liberal, TYP_REF, fgCurHeapVN, elemTypeEqVN);
+    ValueNum  hAtArrType           = vnStore->VNForMapSelect(VNK_Liberal, TYP_REF, fgCurMemoryVN[GcHeap], elemTypeEqVN);
     ValueNum  hAtArrTypeAtArr      = vnStore->VNForMapSelect(VNK_Liberal, TYP_REF, hAtArrType, arrVN);
     ValueNum  hAtArrTypeAtArrAtInx = vnStore->VNForMapSelect(VNK_Liberal, arrElemType, hAtArrTypeAtArr, inxVN);
 
@@ -2779,7 +2780,7 @@ ValueNum Compiler::fgValueNumberArrIndexAssign(CORINFO_CLASS_HANDLE elemTypeEq,
 #ifdef DEBUG
     if (verbose)
     {
-        printf("  hAtArrType " STR_VN "%x is MapSelect(curHeap(" STR_VN "%x), ", hAtArrType, fgCurHeapVN);
+        printf("  hAtArrType " STR_VN "%x is MapSelect(curGcHeap(" STR_VN "%x), ", hAtArrType, fgCurMemoryVN[GcHeap]);
 
         if (arrElemType == TYP_STRUCT)
         {
@@ -2809,11 +2810,11 @@ ValueNum Compiler::fgValueNumberArrIndexAssign(CORINFO_CLASS_HANDLE elemTypeEq,
         vnStore->vnDump(this, newValAtArrType);
         printf("\n");
 
-        printf("  fgCurHeapVN assigned:\n");
+        printf("  fgCurMemoryVN assigned:\n");
     }
 #endif // DEBUG
 
-    return vnStore->VNForMapStore(TYP_REF, fgCurHeapVN, elemTypeEqVN, newValAtArrType);
+    return vnStore->VNForMapStore(TYP_REF, fgCurMemoryVN[GcHeap], elemTypeEqVN, newValAtArrType);
 }
 
 ValueNum Compiler::fgValueNumberArrIndexVal(GenTreePtr tree, VNFuncApp* pFuncApp, ValueNum addrXvn)
@@ -2866,14 +2867,15 @@ ValueNum Compiler::fgValueNumberArrIndexVal(GenTreePtr           tree,
     else
     {
         ValueNum elemTypeEqVN    = vnStore->VNForHandle(ssize_t(elemTypeEq), GTF_ICON_CLASS_HDL);
-        ValueNum hAtArrType      = vnStore->VNForMapSelect(VNK_Liberal, TYP_REF, fgCurHeapVN, elemTypeEqVN);
+        ValueNum hAtArrType      = vnStore->VNForMapSelect(VNK_Liberal, TYP_REF, fgCurMemoryVN[GcHeap], elemTypeEqVN);
         ValueNum hAtArrTypeAtArr = vnStore->VNForMapSelect(VNK_Liberal, TYP_REF, hAtArrType, arrVN);
         ValueNum wholeElem       = vnStore->VNForMapSelect(VNK_Liberal, elemTyp, hAtArrTypeAtArr, inxVN);
 
 #ifdef DEBUG
         if (verbose)
         {
-            printf("  hAtArrType " STR_VN "%x is MapSelect(curHeap(" STR_VN "%x), ", hAtArrType, fgCurHeapVN);
+            printf("  hAtArrType " STR_VN "%x is MapSelect(curGcHeap(" STR_VN "%x), ", hAtArrType,
+                   fgCurMemoryVN[GcHeap]);
             if (elemTyp == TYP_STRUCT)
             {
                 printf("%s[]).\n", eeGetClassName(elemTypeEq));
@@ -4197,10 +4199,10 @@ void Compiler::fgValueNumber()
     else
     {
         ValueNumPair noVnp;
-        // Make sure the heap SSA names have no value numbers.
-        for (unsigned i = 0; i < lvHeapNumSsaNames; i++)
+        // Make sure the memory SSA names have no value numbers.
+        for (unsigned i = 0; i < lvMemoryNumSsaNames; i++)
         {
-            lvHeapPerSsaData.GetRef(i).m_vnPair = noVnp;
+            lvMemoryPerSsaData.GetRef(i).m_vnPair = noVnp;
         }
         for (BasicBlock* blk = fgFirstBB; blk != nullptr; blk = blk->bbNext)
         {
@@ -4306,13 +4308,13 @@ void Compiler::fgValueNumber()
             ssaDef->m_defLoc.m_blk = fgFirstBB;
         }
     }
-    // Give "Heap" an initial value number (about which we know nothing).
-    ValueNum heapInitVal = vnStore->VNForFunc(TYP_REF, VNF_InitVal, vnStore->VNForIntCon(-1)); // Use -1 for the heap.
-    GetHeapPerSsaData(SsaConfig::FIRST_SSA_NUM)->m_vnPair.SetBoth(heapInitVal);
+    // Give memory an initial value number (about which we know nothing).
+    ValueNum memoryInitVal = vnStore->VNForFunc(TYP_REF, VNF_InitVal, vnStore->VNForIntCon(-1)); // Use -1 for memory.
+    GetMemoryPerSsaData(SsaConfig::FIRST_SSA_NUM)->m_vnPair.SetBoth(memoryInitVal);
 #ifdef DEBUG
     if (verbose)
     {
-        printf("Heap Initial Value in BB01 is: " STR_VN "%x\n", heapInitVal);
+        printf("Memory Initial Value in BB01 is: " STR_VN "%x\n", memoryInitVal);
     }
 #endif // DEBUG
 
@@ -4485,32 +4487,32 @@ void Compiler::fgValueNumberBlock(BasicBlock* blk)
         }
     }
 
-    // Now do the same for "Heap".
+    // Now do the same for "GcHeap".
     // Is there a phi for this block?
-    if (blk->bbHeapSsaPhiFunc == nullptr)
+    if (blk->bbMemorySsaPhiFunc[GcHeap] == nullptr)
     {
-        fgCurHeapVN = GetHeapPerSsaData(blk->bbHeapSsaNumIn)->m_vnPair.GetLiberal();
-        assert(fgCurHeapVN != ValueNumStore::NoVN);
+        fgCurMemoryVN[GcHeap] = GetMemoryPerSsaData(blk->bbMemorySsaNumIn[GcHeap])->m_vnPair.GetLiberal();
+        assert(fgCurMemoryVN[GcHeap] != ValueNumStore::NoVN);
     }
     else
     {
         unsigned loopNum;
-        ValueNum newHeapVN;
+        ValueNum newGcHeapVN;
         if (optBlockIsLoopEntry(blk, &loopNum))
         {
-            newHeapVN = fgHeapVNForLoopSideEffects(blk, loopNum);
+            newGcHeapVN = fgMemoryVNForLoopSideEffects(GcHeap, blk, loopNum);
         }
         else
         {
             // Are all the VN's the same?
-            BasicBlock::HeapPhiArg* phiArgs = blk->bbHeapSsaPhiFunc;
-            assert(phiArgs != BasicBlock::EmptyHeapPhiDef);
+            BasicBlock::MemoryPhiArg* phiArgs = blk->bbMemorySsaPhiFunc[GcHeap];
+            assert(phiArgs != BasicBlock::EmptyMemoryPhiDef);
             // There should be > 1 args to a phi.
             assert(phiArgs->m_nextArg != nullptr);
             ValueNum phiAppVN = vnStore->VNForIntCon(phiArgs->GetSsaNum());
             JITDUMP("  Building phi application: $%x = SSA# %d.\n", phiAppVN, phiArgs->GetSsaNum());
             bool     allSame = true;
-            ValueNum sameVN  = GetHeapPerSsaData(phiArgs->GetSsaNum())->m_vnPair.GetLiberal();
+            ValueNum sameVN  = GetMemoryPerSsaData(phiArgs->GetSsaNum())->m_vnPair.GetLiberal();
             if (sameVN == ValueNumStore::NoVN)
             {
                 allSame = false;
@@ -4518,7 +4520,7 @@ void Compiler::fgValueNumberBlock(BasicBlock* blk)
             phiArgs = phiArgs->m_nextArg;
             while (phiArgs != nullptr)
             {
-                ValueNum phiArgVN = GetHeapPerSsaData(phiArgs->GetSsaNum())->m_vnPair.GetLiberal();
+                ValueNum phiArgVN = GetMemoryPerSsaData(phiArgs->GetSsaNum())->m_vnPair.GetLiberal();
                 if (phiArgVN == ValueNumStore::NoVN || phiArgVN != sameVN)
                 {
                     allSame = false;
@@ -4535,22 +4537,22 @@ void Compiler::fgValueNumberBlock(BasicBlock* blk)
             }
             if (allSame)
             {
-                newHeapVN = sameVN;
+                newGcHeapVN = sameVN;
             }
             else
             {
-                newHeapVN =
-                    vnStore->VNForFunc(TYP_REF, VNF_PhiHeapDef, vnStore->VNForHandle(ssize_t(blk), 0), phiAppVN);
+                newGcHeapVN =
+                    vnStore->VNForFunc(TYP_REF, VNF_PhiMemoryDef, vnStore->VNForHandle(ssize_t(blk), 0), phiAppVN);
             }
         }
-        GetHeapPerSsaData(blk->bbHeapSsaNumIn)->m_vnPair.SetLiberal(newHeapVN);
-        fgCurHeapVN = newHeapVN;
+        GetMemoryPerSsaData(blk->bbMemorySsaNumIn[GcHeap])->m_vnPair.SetLiberal(newGcHeapVN);
+        fgCurMemoryVN[GcHeap] = newGcHeapVN;
     }
 #ifdef DEBUG
     if (verbose)
     {
-        printf("The SSA definition for heap (#%d) at start of BB%02u is ", blk->bbHeapSsaNumIn, blk->bbNum);
-        vnPrint(fgCurHeapVN, 1);
+        printf("The SSA definition for GcHeap (#%d) at start of BB%02u is ", blk->bbMemorySsaNumIn[GcHeap], blk->bbNum);
+        vnPrint(fgCurMemoryVN[GcHeap], 1);
         printf("\n");
     }
 #endif // DEBUG
@@ -4589,15 +4591,17 @@ void Compiler::fgValueNumberBlock(BasicBlock* blk)
 #endif
     }
 
-    if (blk->bbHeapSsaNumOut != blk->bbHeapSsaNumIn)
+    if (blk->bbMemorySsaNumOut[GcHeap] != blk->bbMemorySsaNumIn[GcHeap])
     {
-        GetHeapPerSsaData(blk->bbHeapSsaNumOut)->m_vnPair.SetLiberal(fgCurHeapVN);
+        GetMemoryPerSsaData(blk->bbMemorySsaNumOut[GcHeap])->m_vnPair.SetLiberal(fgCurMemoryVN[GcHeap]);
     }
 
     compCurBB = nullptr;
 }
 
-ValueNum Compiler::fgHeapVNForLoopSideEffects(BasicBlock* entryBlock, unsigned innermostLoopNum)
+ValueNum Compiler::fgMemoryVNForLoopSideEffects(MemoryKind  memoryKind,
+                                                BasicBlock* entryBlock,
+                                                unsigned    innermostLoopNum)
 {
     // "loopNum" is the innermost loop for which "blk" is the entry; find the outermost one.
     assert(innermostLoopNum != BasicBlock::NOT_IN_LOOP);
@@ -4616,27 +4620,27 @@ ValueNum Compiler::fgHeapVNForLoopSideEffects(BasicBlock* entryBlock, unsigned i
 #ifdef DEBUG
     if (verbose)
     {
-        printf("Computing heap state for block BB%02u, entry block for loops %d to %d:\n", entryBlock->bbNum,
-               innermostLoopNum, loopNum);
+        printf("Computing %s state for block BB%02u, entry block for loops %d to %d:\n", memoryKindNames[memoryKind],
+               entryBlock->bbNum, innermostLoopNum, loopNum);
     }
 #endif // DEBUG
 
-    // If this loop has heap havoc effects, just use a new, unique VN.
-    if (optLoopTable[loopNum].lpLoopHasHeapHavoc)
+    // If this loop has memory havoc effects, just use a new, unique VN.
+    if (optLoopTable[loopNum].lpLoopHasMemoryHavoc[memoryKind])
     {
         ValueNum res = vnStore->VNForExpr(entryBlock, TYP_REF);
 #ifdef DEBUG
         if (verbose)
         {
-            printf("  Loop %d has heap havoc effect; heap state is new fresh $%x.\n", loopNum, res);
+            printf("  Loop %d has memory havoc effect; heap state is new fresh $%x.\n", loopNum, res);
         }
 #endif // DEBUG
         return res;
     }
 
     // Otherwise, find the predecessors of the entry block that are not in the loop.
-    // If there is only one such, use its heap value as the "base."  If more than one,
-    // use a new unique heap VN.
+    // If there is only one such, use its memory value as the "base."  If more than one,
+    // use a new unique VN.
     BasicBlock* nonLoopPred          = nullptr;
     bool        multipleNonLoopPreds = false;
     for (flowList* pred = BlockPredsWithEH(entryBlock); pred != nullptr; pred = pred->flNext)
@@ -4668,126 +4672,131 @@ ValueNum Compiler::fgHeapVNForLoopSideEffects(BasicBlock* entryBlock, unsigned i
 #ifdef DEBUG
         if (verbose)
         {
-            printf("  Therefore, heap state is new, fresh $%x.\n", res);
+            printf("  Therefore, memory state is new, fresh $%x.\n", res);
         }
 #endif // DEBUG
         return res;
     }
     // Otherwise, there is a single non-loop pred.
     assert(nonLoopPred != nullptr);
-    // What is it's heap post-state?
-    ValueNum newHeapVN = GetHeapPerSsaData(nonLoopPred->bbHeapSsaNumOut)->m_vnPair.GetLiberal();
-    assert(newHeapVN !=
+    // What is its memory post-state?
+    ValueNum newMemoryVN = GetMemoryPerSsaData(nonLoopPred->bbMemorySsaNumOut[memoryKind])->m_vnPair.GetLiberal();
+    assert(newMemoryVN !=
            ValueNumStore::NoVN); // We must have processed the single non-loop pred before reaching the loop entry.
 
 #ifdef DEBUG
     if (verbose)
     {
-        printf("  Init heap state is $%x, with new, fresh VN at:\n", newHeapVN);
+        printf("  Init %s state is $%x, with new, fresh VN at:\n", memoryKindNames[memoryKind], newMemoryVN);
     }
 #endif // DEBUG
     // Modify "base" by setting all the modified fields/field maps/array maps to unknown values.
-    // First the fields/field maps.
-
-    Compiler::LoopDsc::FieldHandleSet* fieldsMod = optLoopTable[loopNum].lpFieldsModified;
-    if (fieldsMod != nullptr)
+    // These annotations apply specifically to the GcHeap, where we disambiguate across such stores.
+    if (memoryKind == GcHeap)
     {
-        for (Compiler::LoopDsc::FieldHandleSet::KeyIterator ki = fieldsMod->Begin(); !ki.Equal(fieldsMod->End()); ++ki)
+        // First the fields/field maps.
+        Compiler::LoopDsc::FieldHandleSet* fieldsMod = optLoopTable[loopNum].lpFieldsModified;
+        if (fieldsMod != nullptr)
         {
-            CORINFO_FIELD_HANDLE fldHnd   = ki.Get();
-            ValueNum             fldHndVN = vnStore->VNForHandle(ssize_t(fldHnd), GTF_ICON_FIELD_HDL);
+            for (Compiler::LoopDsc::FieldHandleSet::KeyIterator ki = fieldsMod->Begin(); !ki.Equal(fieldsMod->End());
+                 ++ki)
+            {
+                CORINFO_FIELD_HANDLE fldHnd   = ki.Get();
+                ValueNum             fldHndVN = vnStore->VNForHandle(ssize_t(fldHnd), GTF_ICON_FIELD_HDL);
 
 #ifdef DEBUG
-            if (verbose)
-            {
-                const char* modName;
-                const char* fldName = eeGetFieldName(fldHnd, &modName);
-                printf("     VNForHandle(Fseq[%s]) is " STR_VN "%x\n", fldName, fldHndVN);
+                if (verbose)
+                {
+                    const char* modName;
+                    const char* fldName = eeGetFieldName(fldHnd, &modName);
+                    printf("     VNForHandle(Fseq[%s]) is " STR_VN "%x\n", fldName, fldHndVN);
 
-                printf("  fgCurHeapVN assigned:\n");
-            }
+                    printf("  fgCurMemoryVN assigned:\n");
+                }
 #endif // DEBUG
 
-            newHeapVN = vnStore->VNForMapStore(TYP_REF, newHeapVN, fldHndVN, vnStore->VNForExpr(entryBlock, TYP_REF));
+                newMemoryVN =
+                    vnStore->VNForMapStore(TYP_REF, newMemoryVN, fldHndVN, vnStore->VNForExpr(entryBlock, TYP_REF));
+            }
         }
-    }
-    // Now do the array maps.
-    Compiler::LoopDsc::ClassHandleSet* elemTypesMod = optLoopTable[loopNum].lpArrayElemTypesModified;
-    if (elemTypesMod != nullptr)
-    {
-        for (Compiler::LoopDsc::ClassHandleSet::KeyIterator ki = elemTypesMod->Begin(); !ki.Equal(elemTypesMod->End());
-             ++ki)
+        // Now do the array maps.
+        Compiler::LoopDsc::ClassHandleSet* elemTypesMod = optLoopTable[loopNum].lpArrayElemTypesModified;
+        if (elemTypesMod != nullptr)
         {
-            CORINFO_CLASS_HANDLE elemClsHnd = ki.Get();
+            for (Compiler::LoopDsc::ClassHandleSet::KeyIterator ki = elemTypesMod->Begin();
+                 !ki.Equal(elemTypesMod->End()); ++ki)
+            {
+                CORINFO_CLASS_HANDLE elemClsHnd = ki.Get();
 
 #ifdef DEBUG
-            if (verbose)
-            {
-                var_types elemTyp = DecodeElemType(elemClsHnd);
-                if (varTypeIsStruct(elemTyp))
-                {
-                    printf("     Array map %s[]\n", eeGetClassName(elemClsHnd));
-                }
-                else
+                if (verbose)
                 {
-                    printf("     Array map %s[]\n", varTypeName(elemTyp));
+                    var_types elemTyp = DecodeElemType(elemClsHnd);
+                    if (varTypeIsStruct(elemTyp))
+                    {
+                        printf("     Array map %s[]\n", eeGetClassName(elemClsHnd));
+                    }
+                    else
+                    {
+                        printf("     Array map %s[]\n", varTypeName(elemTyp));
+                    }
+                    printf("  fgCurMemoryVN assigned:\n");
                 }
-                printf("  fgCurHeapVN assigned:\n");
-            }
 #endif // DEBUG
 
-            ValueNum elemTypeVN = vnStore->VNForHandle(ssize_t(elemClsHnd), GTF_ICON_CLASS_HDL);
-            ValueNum uniqueVN   = vnStore->VNForExpr(entryBlock, TYP_REF);
-            newHeapVN           = vnStore->VNForMapStore(TYP_REF, newHeapVN, elemTypeVN, uniqueVN);
+                ValueNum elemTypeVN = vnStore->VNForHandle(ssize_t(elemClsHnd), GTF_ICON_CLASS_HDL);
+                ValueNum uniqueVN   = vnStore->VNForExpr(entryBlock, TYP_REF);
+                newMemoryVN         = vnStore->VNForMapStore(TYP_REF, newMemoryVN, elemTypeVN, uniqueVN);
+            }
         }
     }
 
 #ifdef DEBUG
     if (verbose)
     {
-        printf("  Final heap state is $%x.\n", newHeapVN);
+        printf("  Final %s state is $%x.\n", memoryKindNames[memoryKind], newMemoryVN);
     }
 #endif // DEBUG
-    return newHeapVN;
+    return newMemoryVN;
 }
 
-void Compiler::fgMutateHeap(GenTreePtr tree DEBUGARG(const char* msg))
+void Compiler::fgMutateGcHeap(GenTreePtr tree DEBUGARG(const char* msg))
 {
-    // Update the current heap VN, and if we're tracking the heap SSA # caused by this node, record it.
-    recordHeapStore(tree, vnStore->VNForExpr(compCurBB, TYP_REF) DEBUGARG(msg));
+    // Update the current memory VN, and if we're tracking the heap SSA # caused by this node, record it.
+    recordGcHeapStore(tree, vnStore->VNForExpr(compCurBB, TYP_REF) DEBUGARG(msg));
 }
 
-void Compiler::recordHeapStore(GenTreePtr curTree, ValueNum heapVN DEBUGARG(const char* msg))
+void Compiler::recordGcHeapStore(GenTreePtr curTree, ValueNum gcHeapVN DEBUGARG(const char* msg))
 {
-    // bbHeapDef must be set to true for any block that Mutates the global Heap
-    assert(compCurBB->bbHeapDef);
-    fgCurHeapVN = heapVN;
+    // bbMemoryDef must include GcHeap for any block that mutates the GC Heap
+    assert((compCurBB->bbMemoryDef & memoryKindSet(GcHeap)) != 0);
+    fgCurMemoryVN[GcHeap] = gcHeapVN;
 
 #ifdef DEBUG
     if (verbose)
     {
-        printf("  fgCurHeapVN assigned by %s at ", msg);
+        printf("  fgCurMemoryVN[GcHeap] assigned by %s at ", msg);
         Compiler::printTreeID(curTree);
-        printf(" to VN: " STR_VN "%x.\n", heapVN);
+        printf(" to VN: " STR_VN "%x.\n", gcHeapVN);
     }
 #endif // DEBUG
 
-    fgValueNumberRecordHeapSsa(curTree);
+    fgValueNumberRecordMemorySsa(GcHeap, curTree);
 }
 
-void Compiler::fgValueNumberRecordHeapSsa(GenTreePtr tree)
+void Compiler::fgValueNumberRecordMemorySsa(MemoryKind memoryKind, GenTreePtr tree)
 {
     unsigned ssaNum;
-    if (GetHeapSsaMap()->Lookup(tree, &ssaNum))
+    if (GetMemorySsaMap(memoryKind)->Lookup(tree, &ssaNum))
     {
-        GetHeapPerSsaData(ssaNum)->m_vnPair.SetLiberal(fgCurHeapVN);
+        GetMemoryPerSsaData(ssaNum)->m_vnPair.SetLiberal(fgCurMemoryVN[memoryKind]);
 #ifdef DEBUG
         if (verbose)
         {
             printf("Node ");
             Compiler::printTreeID(tree);
-            printf(" sets heap SSA # %d to VN $%x: ", ssaNum, fgCurHeapVN);
-            vnStore->vnDump(this, fgCurHeapVN);
+            printf(" sets %s SSA # %d to VN $%x: ", memoryKindNames[memoryKind], ssaNum, fgCurMemoryVN[memoryKind]);
+            vnStore->vnDump(this, fgCurMemoryVN[memoryKind]);
             printf("\n");
         }
 #endif // DEBUG
@@ -4891,8 +4900,8 @@ void Compiler::fgValueNumberBlockAssignment(GenTreePtr tree, bool evalAsgLhsInd)
     GenTree* lhs = tree->gtGetOp1();
     GenTree* rhs = tree->gtGetOp2();
 #ifdef DEBUG
-    // Sometimes we query the heap ssa map, and need a dummy location for the ignored result.
-    unsigned heapSsaNum;
+    // Sometimes we query the memory ssa map in an assertion, and need a dummy location for the ignored result.
+    unsigned memorySsaNum;
 #endif
 
     if (tree->OperIsInitBlkOp())
@@ -4903,8 +4912,8 @@ void Compiler::fgValueNumberBlockAssignment(GenTreePtr tree, bool evalAsgLhsInd)
         if (tree->DefinesLocal(this, &lclVarTree, &isEntire))
         {
             assert(lclVarTree->gtFlags & GTF_VAR_DEF);
-            // Should not have been recorded as updating the heap.
-            assert(!GetHeapSsaMap()->Lookup(tree, &heapSsaNum));
+            // Should not have been recorded as updating the GC heap.
+            assert(!GetMemorySsaMap(GcHeap)->Lookup(tree, &memorySsaNum));
 
             unsigned lclNum = lclVarTree->GetLclNum();
 
@@ -4945,9 +4954,9 @@ void Compiler::fgValueNumberBlockAssignment(GenTreePtr tree, bool evalAsgLhsInd)
         }
         else
         {
-            // For now, arbitrary side effect on Heap.
+            // For now, arbitrary side effect on GcHeap.
             // TODO-CQ: Why not be complete, and get this case right?
-            fgMutateHeap(tree DEBUGARG("INITBLK - non local"));
+            fgMutateGcHeap(tree DEBUGARG("INITBLK - non local"));
         }
         // Initblock's are of type void.  Give them the void "value" -- they may occur in argument lists, which we
         // want to be able to give VN's to.
@@ -4957,7 +4966,7 @@ void Compiler::fgValueNumberBlockAssignment(GenTreePtr tree, bool evalAsgLhsInd)
     {
         assert(tree->OperIsCopyBlkOp());
         // TODO-Cleanup: We should factor things so that we uniformly rely on "PtrTo" VN's, and
-        // the heap cases can be shared with assignments.
+        // the memory cases can be shared with assignments.
         GenTreeLclVarCommon* lclVarTree = nullptr;
         bool                 isEntire   = false;
         // Note that we don't care about exceptions here, since we're only using the values
@@ -4965,8 +4974,8 @@ void Compiler::fgValueNumberBlockAssignment(GenTreePtr tree, bool evalAsgLhsInd)
 
         if (tree->DefinesLocal(this, &lclVarTree, &isEntire))
         {
-            // Should not have been recorded as updating the heap.
-            assert(!GetHeapSsaMap()->Lookup(tree, &heapSsaNum));
+            // Should not have been recorded as updating the GC heap.
+            assert(!GetMemorySsaMap(GcHeap)->Lookup(tree, &memorySsaNum));
 
             unsigned      lhsLclNum = lclVarTree->GetLclNum();
             FieldSeqNode* lhsFldSeq = nullptr;
@@ -5083,10 +5092,10 @@ void Compiler::fgValueNumberBlockAssignment(GenTreePtr tree, bool evalAsgLhsInd)
 
                             if (fldSeqForStaticVar != FieldSeqStore::NotAField())
                             {
-                                // We model statics as indices into the heap variable.
+                                // We model statics as indices into the GcHeap.
                                 ValueNum selectedStaticVar;
                                 size_t   structSize = 0;
-                                selectedStaticVar   = vnStore->VNApplySelectors(VNK_Liberal, fgCurHeapVN,
+                                selectedStaticVar   = vnStore->VNApplySelectors(VNK_Liberal, fgCurMemoryVN[GcHeap],
                                                                               fldSeqForStaticVar, &structSize);
                                 selectedStaticVar =
                                     vnStore->VNApplySelectorsTypeCheck(selectedStaticVar, indType, structSize);
@@ -5166,9 +5175,9 @@ void Compiler::fgValueNumberBlockAssignment(GenTreePtr tree, bool evalAsgLhsInd)
         }
         else
         {
-            // For now, arbitrary side effect on Heap.
+            // For now, arbitrary side effect on GcHeap.
             // TODO-CQ: Why not be complete, and get this case right?
-            fgMutateHeap(tree DEBUGARG("COPYBLK - non local"));
+            fgMutateGcHeap(tree DEBUGARG("COPYBLK - non local"));
         }
         // Copyblock's are of type void.  Give them the void "value" -- they may occur in argument lists, which we want
         // to be able to give VN's to.
@@ -5367,11 +5376,11 @@ void Compiler::fgValueNumberTree(GenTreePtr tree, bool evalAsgLhsInd)
 
                     if (isVolatile)
                     {
-                        // For Volatile indirection, first mutate the global heap
-                        fgMutateHeap(tree DEBUGARG("GTF_FLD_VOLATILE - read"));
+                        // For Volatile indirection, first mutate GcHeap
+                        fgMutateGcHeap(tree DEBUGARG("GTF_FLD_VOLATILE - read"));
                     }
 
-                    // We just mutate the heap if isVolatile is true, and then do the read as normal.
+                    // We just mutate GcHeap if isVolatile is true, and then do the read as normal.
                     //
                     // This allows:
                     //   1: read s;
@@ -5400,13 +5409,13 @@ void Compiler::fgValueNumberTree(GenTreePtr tree, bool evalAsgLhsInd)
                     else
                     {
                         // This is a reference to heap memory.
-                        // We model statics as indices into the heap variable.
+                        // We model statics as indices into the GC heap variable.
 
                         FieldSeqNode* fldSeqForStaticVar =
                             GetFieldSeqStore()->CreateSingleton(tree->gtClsVar.gtClsVarHnd);
                         size_t structSize = 0;
-                        selectedStaticVar =
-                            vnStore->VNApplySelectors(VNK_Liberal, fgCurHeapVN, fldSeqForStaticVar, &structSize);
+                        selectedStaticVar = vnStore->VNApplySelectors(VNK_Liberal, fgCurMemoryVN[GcHeap],
+                                                                      fldSeqForStaticVar, &structSize);
                         selectedStaticVar =
                             vnStore->VNApplySelectorsTypeCheck(selectedStaticVar, tree->TypeGet(), structSize);
 
@@ -5430,8 +5439,8 @@ void Compiler::fgValueNumberTree(GenTreePtr tree, bool evalAsgLhsInd)
                 break;
 
             case GT_MEMORYBARRIER: // Leaf
-                // For MEMORYBARRIER add an arbitrary side effect on Heap.
-                fgMutateHeap(tree DEBUGARG("MEMORYBARRIER"));
+                // For MEMORYBARRIER add an arbitrary side effect on GcHeap.
+                fgMutateGcHeap(tree DEBUGARG("MEMORYBARRIER"));
                 break;
 
             // These do not represent values.
@@ -5463,8 +5472,8 @@ void Compiler::fgValueNumberTree(GenTreePtr tree, bool evalAsgLhsInd)
     else if (GenTree::OperIsSimple(oper))
     {
 #ifdef DEBUG
-        // Sometimes we query the heap ssa map, and need a dummy location for the ignored result.
-        unsigned heapSsaNum;
+        // Sometimes we query the memory ssa map in an assertion, and need a dummy location for the ignored result.
+        unsigned memorySsaNum;
 #endif
 
         if (GenTree::OperIsAssignment(oper) && !varTypeIsStruct(tree))
@@ -5483,7 +5492,7 @@ void Compiler::fgValueNumberTree(GenTreePtr tree, bool evalAsgLhsInd)
                 // If the LHS is an IND, we didn't evaluate it when we visited it previously.
                 // But we didn't know that the parent was an op=.  We do now, so go back and evaluate it.
                 // (We actually check if the effective val is the IND.  We will have evaluated any non-last
-                // args of an LHS comma already -- including their heap effects.)
+                // args of an LHS comma already -- including their memory effects.)
                 GenTreePtr lhsVal = lhs->gtEffectiveVal(/*commaOnly*/ true);
                 if (lhsVal->OperIsIndir() || (lhsVal->OperGet() == GT_CLS_VAR))
                 {
@@ -5568,8 +5577,8 @@ void Compiler::fgValueNumberTree(GenTreePtr tree, bool evalAsgLhsInd)
                     GenTreeLclVarCommon* lcl          = lhs->AsLclVarCommon();
                     unsigned             lclDefSsaNum = GetSsaNumForLocalVarDef(lcl);
 
-                    // Should not have been recorded as updating the heap.
-                    assert(!GetHeapSsaMap()->Lookup(tree, &heapSsaNum));
+                    // Should not have been recorded as updating the GC heap.
+                    assert(!GetMemorySsaMap(GcHeap)->Lookup(tree, &memorySsaNum));
 
                     if (lclDefSsaNum != SsaConfig::RESERVED_SSA_NUM)
                     {
@@ -5611,8 +5620,8 @@ void Compiler::fgValueNumberTree(GenTreePtr tree, bool evalAsgLhsInd)
                     GenTreeLclFld* lclFld       = lhs->AsLclFld();
                     unsigned       lclDefSsaNum = GetSsaNumForLocalVarDef(lclFld);
 
-                    // Should not have been recorded as updating the heap.
-                    assert(!GetHeapSsaMap()->Lookup(tree, &heapSsaNum));
+                    // Should not have been recorded as updating the GC heap.
+                    assert(!GetMemorySsaMap(GcHeap)->Lookup(tree, &memorySsaNum));
 
                     if (lclDefSsaNum != SsaConfig::RESERVED_SSA_NUM)
                     {
@@ -5679,8 +5688,8 @@ void Compiler::fgValueNumberTree(GenTreePtr tree, bool evalAsgLhsInd)
 
                     if (isVolatile)
                     {
-                        // For Volatile store indirection, first mutate the global heap
-                        fgMutateHeap(lhs DEBUGARG("GTF_IND_VOLATILE - store"));
+                        // For Volatile store indirection, first mutate GcHeap
+                        fgMutateGcHeap(lhs DEBUGARG("GTF_IND_VOLATILE - store"));
                         tree->gtVNPair.SetBoth(vnStore->VNForExpr(compCurBB, lhs->TypeGet()));
                     }
 
@@ -5835,7 +5844,7 @@ void Compiler::fgValueNumberTree(GenTreePtr tree, bool evalAsgLhsInd)
 
                             ValueNum heapVN = fgValueNumberArrIndexAssign(elemTypeEq, arrVN, inxVN, fldSeq,
                                                                           rhsVNPair.GetLiberal(), lhs->TypeGet());
-                            recordHeapStore(tree, heapVN DEBUGARG("Array element assignment"));
+                            recordGcHeapStore(tree, heapVN DEBUGARG("Array element assignment"));
                         }
                         // It may be that we haven't parsed it yet.  Try.
                         else if (lhs->gtFlags & GTF_IND_ARR_INDEX)
@@ -5852,7 +5861,7 @@ void Compiler::fgValueNumberTree(GenTreePtr tree, bool evalAsgLhsInd)
                             arg->ParseArrayAddress(this, &arrInfo, &arr, &inxVN, &fldSeq);
                             if (arr == nullptr)
                             {
-                                fgMutateHeap(tree DEBUGARG("assignment to unparseable array expression"));
+                                fgMutateGcHeap(tree DEBUGARG("assignment to unparseable array expression"));
                                 return;
                             }
                             // Otherwise, parsing succeeded.
@@ -5872,13 +5881,13 @@ void Compiler::fgValueNumberTree(GenTreePtr tree, bool evalAsgLhsInd)
 
                             ValueNum heapVN = fgValueNumberArrIndexAssign(elemTypeEq, arrVN, inxVN, fldSeq,
                                                                           rhsVNPair.GetLiberal(), lhs->TypeGet());
-                            recordHeapStore(tree, heapVN DEBUGARG("assignment to unparseable array expression"));
+                            recordGcHeapStore(tree, heapVN DEBUGARG("assignment to unparseable array expression"));
                         }
                         else if (arg->IsFieldAddr(this, &obj, &staticOffset, &fldSeq))
                         {
                             if (fldSeq == FieldSeqStore::NotAField())
                             {
-                                fgMutateHeap(tree DEBUGARG("NotAField"));
+                                fgMutateGcHeap(tree DEBUGARG("NotAField"));
                             }
                             else
                             {
@@ -5893,8 +5902,8 @@ void Compiler::fgValueNumberTree(GenTreePtr tree, bool evalAsgLhsInd)
                                     assert(staticOffset == nullptr);
                                 }
 #endif // DEBUG
-                                // Get the first (instance or static) field from field seq.  Heap[field] will yield the
-                                // "field map".
+                                // Get the first (instance or static) field from field seq.  GcHeap[field] will yield
+                                // the "field map".
                                 if (fldSeq->IsFirstElemFieldSeq())
                                 {
                                     fldSeq = fldSeq->m_next;
@@ -5907,7 +5916,8 @@ void Compiler::fgValueNumberTree(GenTreePtr tree, bool evalAsgLhsInd)
 
                                 // The final field in the sequence will need to match the 'indType'
                                 var_types indType = lhs->TypeGet();
-                                ValueNum fldMapVN = vnStore->VNApplySelectors(VNK_Liberal, fgCurHeapVN, firstFieldOnly);
+                                ValueNum  fldMapVN =
+                                    vnStore->VNApplySelectors(VNK_Liberal, fgCurMemoryVN[GcHeap], firstFieldOnly);
 
                                 // The type of the field is "struct" if there are more fields in the sequence,
                                 // otherwise it is the type returned from VNApplySelectors above.
@@ -5963,8 +5973,8 @@ void Compiler::fgValueNumberTree(GenTreePtr tree, bool evalAsgLhsInd)
                                                                             storeVal, indType, compCurBB);
                                     }
 
-                                    newFldMapVN = vnStore->VNApplySelectorsAssign(VNK_Liberal, fgCurHeapVN, fldSeq,
-                                                                                  storeVal, indType, compCurBB);
+                                    newFldMapVN = vnStore->VNApplySelectorsAssign(VNK_Liberal, fgCurMemoryVN[GcHeap],
+                                                                                  fldSeq, storeVal, indType, compCurBB);
                                 }
 
                                 // It is not strictly necessary to set the lhs value number,
@@ -5974,18 +5984,18 @@ void Compiler::fgValueNumberTree(GenTreePtr tree, bool evalAsgLhsInd)
 #ifdef DEBUG
                                 if (verbose)
                                 {
-                                    printf("  fgCurHeapVN assigned:\n");
+                                    printf("  fgCurMemoryVN assigned:\n");
                                 }
 #endif // DEBUG
-                                // bbHeapDef must be set to true for any block that Mutates the global Heap
-                                assert(compCurBB->bbHeapDef);
+                                // bbMemoryDef must include GcHeap for any block that mutates the GC heap
+                                assert((compCurBB->bbMemoryDef & memoryKindSet(GcHeap)) != 0);
 
-                                // Update the field map for firstField in Heap to this new value.
+                                // Update the field map for firstField in GcHeap to this new value.
                                 ValueNum heapVN =
-                                    vnStore->VNApplySelectorsAssign(VNK_Liberal, fgCurHeapVN, firstFieldOnly,
+                                    vnStore->VNApplySelectorsAssign(VNK_Liberal, fgCurMemoryVN[GcHeap], firstFieldOnly,
                                                                     newFldMapVN, indType, compCurBB);
 
-                                recordHeapStore(tree, heapVN DEBUGARG("StoreField"));
+                                recordGcHeapStore(tree, heapVN DEBUGARG("StoreField"));
                             }
                         }
                         else
@@ -5993,8 +6003,8 @@ void Compiler::fgValueNumberTree(GenTreePtr tree, bool evalAsgLhsInd)
                             GenTreeLclVarCommon* dummyLclVarTree = nullptr;
                             if (!tree->DefinesLocal(this, &dummyLclVarTree))
                             {
-                                // If it doesn't define a local, then it might update the heap.
-                                fgMutateHeap(tree DEBUGARG("assign-of-IND"));
+                                // If it doesn't define a local, then it might update GcHeap.
+                                fgMutateGcHeap(tree DEBUGARG("assign-of-IND"));
                             }
                         }
                     }
@@ -6010,17 +6020,17 @@ void Compiler::fgValueNumberTree(GenTreePtr tree, bool evalAsgLhsInd)
 
                     if (isVolatile)
                     {
-                        // For Volatile store indirection, first mutate the global heap
-                        fgMutateHeap(lhs DEBUGARG("GTF_CLS_VAR - store")); // always change fgCurHeapVN
+                        // For Volatile store indirection, first mutate GcHeap
+                        fgMutateGcHeap(lhs DEBUGARG("GTF_CLS_VAR - store")); // always change fgCurMemoryVN
                     }
 
-                    // We model statics as indices into the heap variable.
+                    // We model statics as indices into the GC heap variable.
                     FieldSeqNode* fldSeqForStaticVar = GetFieldSeqStore()->CreateSingleton(lhs->gtClsVar.gtClsVarHnd);
                     assert(fldSeqForStaticVar != FieldSeqStore::NotAField());
 
                     ValueNum storeVal = rhsVNPair.GetLiberal(); // The value number from the rhs of the assignment
-                    storeVal = vnStore->VNApplySelectorsAssign(VNK_Liberal, fgCurHeapVN, fldSeqForStaticVar, storeVal,
-                                                               lhs->TypeGet(), compCurBB);
+                    storeVal = vnStore->VNApplySelectorsAssign(VNK_Liberal, fgCurMemoryVN[GcHeap], fldSeqForStaticVar,
+                                                               storeVal, lhs->TypeGet(), compCurBB);
 
                     // It is not strictly necessary to set the lhs value number,
                     // but the dumps read better with it set to the 'storeVal' that we just computed
@@ -6028,22 +6038,22 @@ void Compiler::fgValueNumberTree(GenTreePtr tree, bool evalAsgLhsInd)
 #ifdef DEBUG
                     if (verbose)
                     {
-                        printf("  fgCurHeapVN assigned:\n");
+                        printf("  fgCurMemoryVN assigned:\n");
                     }
 #endif // DEBUG
-                    // bbHeapDef must be set to true for any block that Mutates the global Heap
-                    assert(compCurBB->bbHeapDef);
+                    // bbMemoryDef must include GcHeap for any block that mutates the GC heap
+                    assert((compCurBB->bbMemoryDef & memoryKindSet(GcHeap)) != 0);
 
-                    // Update the field map for the fgCurHeapVN
-                    recordHeapStore(tree, storeVal DEBUGARG("Static Field store"));
+                    // Update the field map for the fgCurMemoryVN and SSA for the tree
+                    recordGcHeapStore(tree, storeVal DEBUGARG("Static Field store"));
                 }
                 break;
 
                 default:
                     assert(!"Unknown node for lhs of assignment!");
 
-                    // For Unknown stores, mutate the global heap
-                    fgMutateHeap(lhs DEBUGARG("Unkwown Assignment - store")); // always change fgCurHeapVN
+                    // For Unknown stores, mutate GcHeap
+                    fgMutateGcHeap(lhs DEBUGARG("Unkwown Assignment - store")); // always change fgCurMemoryVN
                     break;
             }
         }
@@ -6151,8 +6161,8 @@ void Compiler::fgValueNumberTree(GenTreePtr tree, bool evalAsgLhsInd)
             }
             else if (isVolatile)
             {
-                // For Volatile indirection, mutate the global heap
-                fgMutateHeap(tree DEBUGARG("GTF_IND_VOLATILE - read"));
+                // For Volatile indirection, mutate GcHeap
+                fgMutateGcHeap(tree DEBUGARG("GTF_IND_VOLATILE - read"));
 
                 // The value read by the GT_IND can immediately change
                 ValueNum newUniq = vnStore->VNForExpr(compCurBB, tree->TypeGet());
@@ -6300,10 +6310,10 @@ void Compiler::fgValueNumberTree(GenTreePtr tree, bool evalAsgLhsInd)
                     if (fldSeqForStaticVar != FieldSeqStore::NotAField())
                     {
                         ValueNum selectedStaticVar;
-                        // We model statics as indices into the heap variable.
+                        // We model statics as indices into the GC heap variable.
                         size_t structSize = 0;
-                        selectedStaticVar =
-                            vnStore->VNApplySelectors(VNK_Liberal, fgCurHeapVN, fldSeqForStaticVar, &structSize);
+                        selectedStaticVar = vnStore->VNApplySelectors(VNK_Liberal, fgCurMemoryVN[GcHeap],
+                                                                      fldSeqForStaticVar, &structSize);
                         selectedStaticVar = vnStore->VNApplySelectorsTypeCheck(selectedStaticVar, indType, structSize);
 
                         tree->gtVNPair.SetLiberal(selectedStaticVar);
@@ -6328,7 +6338,7 @@ void Compiler::fgValueNumberTree(GenTreePtr tree, bool evalAsgLhsInd)
                     }
                     else if (fldSeq2 != nullptr)
                     {
-                        // Get the first (instance or static) field from field seq.  Heap[field] will yield the "field
+                        // Get the first (instance or static) field from field seq.  GcHeap[field] will yield the "field
                         // map".
                         CLANG_FORMAT_COMMENT_ANCHOR;
 
@@ -6347,7 +6357,7 @@ void Compiler::fgValueNumberTree(GenTreePtr tree, bool evalAsgLhsInd)
                         FieldSeqNode* firstFieldOnly = GetFieldSeqStore()->CreateSingleton(fldSeq2->m_fieldHnd);
                         size_t        structSize     = 0;
                         ValueNum      fldMapVN =
-                            vnStore->VNApplySelectors(VNK_Liberal, fgCurHeapVN, firstFieldOnly, &structSize);
+                            vnStore->VNApplySelectors(VNK_Liberal, fgCurMemoryVN[GcHeap], firstFieldOnly, &structSize);
 
                         // The final field in the sequence will need to match the 'indType'
                         var_types indType = tree->TypeGet();
@@ -6543,8 +6553,8 @@ void Compiler::fgValueNumberTree(GenTreePtr tree, bool evalAsgLhsInd)
                 case GT_LOCKADD: // Binop
                 case GT_XADD:    // Binop
                 case GT_XCHG:    // Binop
-                    // For CMPXCHG and other intrinsics add an arbitrary side effect on Heap.
-                    fgMutateHeap(tree DEBUGARG("Interlocked intrinsic"));
+                    // For CMPXCHG and other intrinsics add an arbitrary side effect on GcHeap.
+                    fgMutateGcHeap(tree DEBUGARG("Interlocked intrinsic"));
                     tree->gtVNPair.SetBoth(vnStore->VNForExpr(compCurBB, tree->TypeGet()));
                     break;
 
@@ -6590,8 +6600,8 @@ void Compiler::fgValueNumberTree(GenTreePtr tree, bool evalAsgLhsInd)
             break;
 
             case GT_CMPXCHG: // Specialop
-                // For CMPXCHG and other intrinsics add an arbitrary side effect on Heap.
-                fgMutateHeap(tree DEBUGARG("Interlocked intrinsic"));
+                // For CMPXCHG and other intrinsics add an arbitrary side effect on GcHeap.
+                fgMutateGcHeap(tree DEBUGARG("Interlocked intrinsic"));
                 tree->gtVNPair.SetBoth(vnStore->VNForExpr(compCurBB, tree->TypeGet()));
                 break;
 
@@ -7025,8 +7035,8 @@ void Compiler::fgValueNumberCall(GenTreeCall* call)
 
         if (modHeap)
         {
-            // For now, arbitrary side effect on Heap.
-            fgMutateHeap(call DEBUGARG("HELPER - modifies heap"));
+            // For now, arbitrary side effect on GcHeap.
+            fgMutateGcHeap(call DEBUGARG("HELPER - modifies heap"));
         }
     }
     else
@@ -7040,8 +7050,8 @@ void Compiler::fgValueNumberCall(GenTreeCall* call)
             call->gtVNPair.SetBoth(vnStore->VNForExpr(compCurBB, call->TypeGet()));
         }
 
-        // For now, arbitrary side effect on Heap.
-        fgMutateHeap(call DEBUGARG("CALL"));
+        // For now, arbitrary side effect on GcHeap.
+        fgMutateGcHeap(call DEBUGARG("CALL"));
     }
 }
 
index c8a57ff..e0443dc 100644 (file)
@@ -217,7 +217,7 @@ private:
 
 #ifdef DEBUG
     // This helps test some performance pathologies related to "evaluation" of VNF_MapSelect terms,
-    // especially relating to the heap.  We count the number of applications of such terms we consider,
+    // especially relating to the GcHeap.  We count the number of applications of such terms we consider,
     // and if this exceeds a limit, indicated by a COMPlus_ variable, we assert.
     unsigned m_numMapSels;
 #endif
@@ -762,7 +762,7 @@ public:
     // the function application it represents; otherwise, return "false."
     bool GetVNFunc(ValueNum vn, VNFuncApp* funcApp);
 
-    // Requires that "vn" represents a "heap address" the sum of a "TYP_REF" value and some integer
+    // Requires that "vn" represents a "GC heap address" the sum of a "TYP_REF" value and some integer
     // value.  Returns the TYP_REF value.
     ValueNum VNForRefInAddr(ValueNum vn);
 
index eb17aed..8c2d2c9 100644 (file)
@@ -18,10 +18,10 @@ ValueNumFuncDef(PtrToLoc, 2, false, false, false)           // Pointer (byref) t
 ValueNumFuncDef(PtrToArrElem, 4, false, false, false)       // Pointer (byref) to an array element.  Args: 0: array elem type eq class var_types value, VN's of: 1: array, 2: index, 3: FieldSeq.
 ValueNumFuncDef(PtrToStatic, 1, false, false, false)        // Pointer (byref) to a static variable (or possibly a field thereof, if the static variable is a struct).  Args: 0: FieldSeq, first element
                                                      // of which is the static var.
-ValueNumFuncDef(Phi, 2, false, false, false)        // A phi function.  Only occurs as arg of PhiDef or PhiHeapDef.  Arguments are SSA numbers of var being defined.
-ValueNumFuncDef(PhiDef, 3, false, false, false)     // Args: 0: local var # (or -1 for Heap), 1: SSA #, 2: VN of definition.
-// Wouldn't need this if I'd made Heap a regular local variable...
-ValueNumFuncDef(PhiHeapDef, 2, false, false, false) // Args: 0: VN for basic block pointer, 1: VN of definition
+ValueNumFuncDef(Phi, 2, false, false, false)        // A phi function.  Only occurs as arg of PhiDef or PhiMemoryDef.  Arguments are SSA numbers of var being defined.
+ValueNumFuncDef(PhiDef, 3, false, false, false)     // Args: 0: local var # (or -1 for memory), 1: SSA #, 2: VN of definition.
+// Wouldn't need this if I'd made memory a regular local variable...
+ValueNumFuncDef(PhiMemoryDef, 2, false, false, false) // Args: 0: VN for basic block pointer, 1: VN of definition
 ValueNumFuncDef(InitVal, 1, false, false, false)    // An input arg, or init val of a local Args: 0: a constant VN.
 
 
index f898d87..b2ebba6 100644 (file)
@@ -17,9 +17,9 @@
 typedef UINT32 ValueNum;
 
 // There are two "kinds" of value numbers, which differ in their modeling of the actions of other threads.
-// "Liberal" value numbers assume that the other threads change contents of heap locations only at
+// "Liberal" value numbers assume that the other threads change contents of memory locations only at
 // synchronization points.  Liberal VNs are appropriate, for example, in identifying CSE opportunities.
-// "Conservative" value numbers assume that the contents of heap locations change arbitrarily between
+// "Conservative" value numbers assume that the contents of memory locations change arbitrarily between
 // every two accesses.  Conservative VNs are appropriate, for example, in assertion prop, where an observation
 // of a property of the value in some storage location is used to perform an optimization downstream on
 // an operation involving the contents of that storage location.  If other threads may modify the storage