summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorJoseph Tremoulet <jotrem@microsoft.com>2016-12-29 17:02:18 -0500
committerJoseph Tremoulet <jotrem@microsoft.com>2017-02-08 09:32:46 -0500
commit463502f9e70e39cd460091298b338a065b8631f2 (patch)
tree6ed7e8bf9a720e23b48c89ec8a8c1ce204f1506b /src
parent5501ecb0855f295c0c60c3743c440badd5d67d61 (diff)
downloadcoreclr-463502f9e70e39cd460091298b338a065b8631f2.tar.gz
coreclr-463502f9e70e39cd460091298b338a065b8631f2.tar.bz2
coreclr-463502f9e70e39cd460091298b338a065b8631f2.zip
Introduce `MemoryKind` abstraction
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.
Diffstat (limited to 'src')
-rw-r--r--src/jit/block.cpp8
-rw-r--r--src/jit/block.h123
-rw-r--r--src/jit/codegenlegacy.cpp72
-rw-r--r--src/jit/compiler.cpp23
-rw-r--r--src/jit/compiler.h76
-rw-r--r--src/jit/compiler.hpp8
-rw-r--r--src/jit/compmemkind.h2
-rw-r--r--src/jit/gentree.cpp19
-rw-r--r--src/jit/liveness.cpp147
-rw-r--r--src/jit/optimizer.cpp62
-rw-r--r--src/jit/ssabuilder.cpp269
-rw-r--r--src/jit/ssabuilder.h4
-rw-r--r--src/jit/ssarenamestate.cpp11
-rw-r--r--src/jit/ssarenamestate.h79
-rw-r--r--src/jit/valuenum.cpp382
-rw-r--r--src/jit/valuenum.h4
-rw-r--r--src/jit/valuenumfuncs.h8
-rw-r--r--src/jit/valuenumtype.h4
18 files changed, 755 insertions, 546 deletions
diff --git a/src/jit/block.cpp b/src/jit/block.cpp
index bb6a57c25b..6d8bc348fd 100644
--- a/src/jit/block.cpp
+++ b/src/jit/block.cpp
@@ -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)
{
diff --git a/src/jit/block.h b/src/jit/block.h
index 3d45ea0171..d95eb9c69e 100644
--- a/src/jit/block.h
+++ b/src/jit/block.h
@@ -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
diff --git a/src/jit/codegenlegacy.cpp b/src/jit/codegenlegacy.cpp
index c249aeb578..a57953a34c 100644
--- a/src/jit/codegenlegacy.cpp
+++ b/src/jit/codegenlegacy.cpp
@@ -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);
}
}
diff --git a/src/jit/compiler.cpp b/src/jit/compiler.cpp
index 2d03085458..3b1db5aeea 100644
--- a/src/jit/compiler.cpp
+++ b/src/jit/compiler.cpp
@@ -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())
diff --git a/src/jit/compiler.h b/src/jit/compiler.h
index 7698731e3f..48624997ec 100644
--- a/src/jit/compiler.h
+++ b/src/jit/compiler.h
@@ -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.
diff --git a/src/jit/compiler.hpp b/src/jit/compiler.hpp
index ca9da82321..6baf601892 100644
--- a/src/jit/compiler.hpp
+++ b/src/jit/compiler.hpp
@@ -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
diff --git a/src/jit/compmemkind.h b/src/jit/compmemkind.h
index e27d2071f7..b22bf6de1e 100644
--- a/src/jit/compmemkind.h
+++ b/src/jit/compmemkind.h
@@ -39,7 +39,7 @@ CompMemKindMacro(IndirAssignMap)
CompMemKindMacro(FieldSeqStore)
CompMemKindMacro(ZeroOffsetFieldMap)
CompMemKindMacro(ArrayInfoMap)
-CompMemKindMacro(HeapPhiArg)
+CompMemKindMacro(MemoryPhiArg)
CompMemKindMacro(CSE)
CompMemKindMacro(GC)
CompMemKindMacro(CorSig)
diff --git a/src/jit/gentree.cpp b/src/jit/gentree.cpp
index 29c9508e12..269a5c2041 100644
--- a/src/jit/gentree.cpp
+++ b/src/jit/gentree.cpp
@@ -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);
diff --git a/src/jit/liveness.cpp b/src/jit/liveness.cpp
index 08e13b694c..bf81bcd864 100644
--- a/src/jit/liveness.cpp
+++ b/src/jit/liveness.cpp
@@ -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");
}
diff --git a/src/jit/optimizer.cpp b/src/jit/optimizer.cpp
index e985d37920..7fce27402f 100644
--- a/src/jit/optimizer.cpp
+++ b/src/jit/optimizer.cpp
@@ -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;
}
}
}
diff --git a/src/jit/ssabuilder.cpp b/src/jit/ssabuilder.cpp
index 5d553c1344..bc7f8f23c4 100644
--- a/src/jit/ssabuilder.cpp
+++ b/src/jit/ssabuilder.cpp
@@ -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
diff --git a/src/jit/ssabuilder.h b/src/jit/ssabuilder.h
index 2fff06573e..e82a4007e3 100644
--- a/src/jit/ssabuilder.h
+++ b/src/jit/ssabuilder.h
@@ -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.
diff --git a/src/jit/ssarenamestate.cpp b/src/jit/ssarenamestate.cpp
index a1e05f192f..07d344dc5c 100644
--- a/src/jit/ssarenamestate.cpp
+++ b/src/jit/ssarenamestate.cpp
@@ -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();
}
}
diff --git a/src/jit/ssarenamestate.h b/src/jit/ssarenamestate.h
index 1db36c5b37..02a9d14c34 100644
--- a/src/jit/ssarenamestate.h
+++ b/src/jit/ssarenamestate.h
@@ -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;
diff --git a/src/jit/valuenum.cpp b/src/jit/valuenum.cpp
index 651b45b43d..37a743ddca 100644
--- a/src/jit/valuenum.cpp
+++ b/src/jit/valuenum.cpp
@@ -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"));
}
}
diff --git a/src/jit/valuenum.h b/src/jit/valuenum.h
index c8a57ff210..e0443dc18c 100644
--- a/src/jit/valuenum.h
+++ b/src/jit/valuenum.h
@@ -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);
diff --git a/src/jit/valuenumfuncs.h b/src/jit/valuenumfuncs.h
index eb17aedf28..8c2d2c958e 100644
--- a/src/jit/valuenumfuncs.h
+++ b/src/jit/valuenumfuncs.h
@@ -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.
diff --git a/src/jit/valuenumtype.h b/src/jit/valuenumtype.h
index f898d87532..b2ebba69c5 100644
--- a/src/jit/valuenumtype.h
+++ b/src/jit/valuenumtype.h
@@ -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