From 463502f9e70e39cd460091298b338a065b8631f2 Mon Sep 17 00:00:00 2001 From: Joseph Tremoulet Date: Thu, 29 Dec 2016 17:02:18 -0500 Subject: [PATCH] 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. --- src/jit/block.cpp | 8 +- src/jit/block.h | 123 ++++++++++++--- src/jit/codegenlegacy.cpp | 72 ++++----- src/jit/compiler.cpp | 23 +-- src/jit/compiler.h | 76 ++++----- src/jit/compiler.hpp | 8 +- src/jit/compmemkind.h | 2 +- src/jit/gentree.cpp | 19 ++- src/jit/liveness.cpp | 147 +++++++++-------- src/jit/optimizer.cpp | 62 +++++--- src/jit/ssabuilder.cpp | 269 +++++++++++++++++-------------- src/jit/ssabuilder.h | 4 +- src/jit/ssarenamestate.cpp | 11 +- src/jit/ssarenamestate.h | 79 ++++++++-- src/jit/valuenum.cpp | 382 +++++++++++++++++++++++---------------------- src/jit/valuenum.h | 4 +- src/jit/valuenumfuncs.h | 8 +- src/jit/valuenumtype.h | 4 +- 18 files changed, 755 insertions(+), 546 deletions(-) diff --git a/src/jit/block.cpp b/src/jit/block.cpp index bb6a57c..6d8bc34 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::GetHashCode(const BasicBlock* ptr) { diff --git a/src/jit/block.h b/src/jit/block.h index 3d45ea0..d95eb9c 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 +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(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 c249aeb..a57953a 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 2d03085..3b1db5a 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(getAllocator()); impSpillCliqueSuccMembers = ExpandArray(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 7698731..4862499 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 ca9da82..6baf601 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 e27d207..b22bf6d 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 29c9508..269a5c2 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 08e13b6..bf81bcd 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 e985d37..7fce274 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(vnStore->ConstantValue(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(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 5d553c1..bc7f8f2 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 2fff065..e82a400 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 a1e05f1..07d344d 100644 --- a/src/jit/ssarenamestate.cpp +++ b/src/jit/ssarenamestate.cpp @@ -32,8 +32,8 @@ SsaRenameState::SsaRenameState(const jitstd::allocator& 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 1db36c5..02a9d14 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 +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) + + // 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(bytes + i * sizeof(T))); + } + + template + ConstructedArray(Args&&... args) + { + for (int i = 0; i < N; ++i) + { + new (bytes + i * sizeof(T), jitstd::placement_t()) T(jitstd::forward(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 memoryStack; + unsigned memoryCount; // Number of stacks/counts to allocate. unsigned lvaCount; diff --git a/src/jit/valuenum.cpp b/src/jit/valuenum.cpp index 651b45b..37a743d 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(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(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 c8a57ff..e0443dc 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 eb17aed..8c2d2c9 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 f898d87..b2ebba6 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 -- 2.7.4