summaryrefslogtreecommitdiff
path: root/src/jit/valuenum.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/jit/valuenum.cpp')
-rw-r--r--src/jit/valuenum.cpp804
1 files changed, 498 insertions, 306 deletions
diff --git a/src/jit/valuenum.cpp b/src/jit/valuenum.cpp
index f7cc0c9a23..aba29c4411 100644
--- a/src/jit/valuenum.cpp
+++ b/src/jit/valuenum.cpp
@@ -1373,10 +1373,10 @@ TailCall:
goto TailCall;
}
}
- else if (funcApp.m_func == VNF_PhiDef || funcApp.m_func == VNF_PhiHeapDef)
+ else if (funcApp.m_func == VNF_PhiDef || funcApp.m_func == VNF_PhiMemoryDef)
{
- unsigned lclNum = BAD_VAR_NUM;
- bool isHeap = false;
+ unsigned lclNum = BAD_VAR_NUM;
+ bool isMemory = false;
VNFuncApp phiFuncApp;
bool defArgIsFunc = false;
if (funcApp.m_func == VNF_PhiDef)
@@ -1386,8 +1386,8 @@ TailCall:
}
else
{
- assert(funcApp.m_func == VNF_PhiHeapDef);
- isHeap = true;
+ assert(funcApp.m_func == VNF_PhiMemoryDef);
+ isMemory = true;
defArgIsFunc = GetVNFunc(funcApp.m_args[1], &phiFuncApp);
}
if (defArgIsFunc && phiFuncApp.m_func == VNF_Phi)
@@ -1401,9 +1401,9 @@ TailCall:
assert(IsVNConstant(phiFuncApp.m_args[0]));
unsigned phiArgSsaNum = ConstantValue<unsigned>(phiFuncApp.m_args[0]);
ValueNum phiArgVN;
- if (isHeap)
+ if (isMemory)
{
- phiArgVN = m_pComp->GetHeapPerSsaData(phiArgSsaNum)->m_vnPair.Get(vnk);
+ phiArgVN = m_pComp->GetMemoryPerSsaData(phiArgSsaNum)->m_vnPair.Get(vnk);
}
else
{
@@ -1430,9 +1430,9 @@ TailCall:
}
assert(IsVNConstant(cur));
phiArgSsaNum = ConstantValue<unsigned>(cur);
- if (isHeap)
+ if (isMemory)
{
- phiArgVN = m_pComp->GetHeapPerSsaData(phiArgSsaNum)->m_vnPair.Get(vnk);
+ phiArgVN = m_pComp->GetMemoryPerSsaData(phiArgSsaNum)->m_vnPair.Get(vnk);
}
else
{
@@ -2465,9 +2465,10 @@ ValueNum ValueNumStore::VNApplySelectorsAssignTypeCoerce(ValueNum elem, var_type
//------------------------------------------------------------------------
// VNApplySelectorsAssign: Compute the value number corresponding to "map" but with
-// the element at "fieldSeq" updated to have type "elem"; this is the new heap
-// value for an assignment of value "elem" into the heap at location "fieldSeq"
-// that occurs in block "block" and has type "indType".
+// the element at "fieldSeq" updated to have type "elem"; this is the new memory
+// value for an assignment of value "elem" into the memory at location "fieldSeq"
+// that occurs in block "block" and has type "indType" (so long as the selectors
+// into that memory occupy disjoint locations, which is true for GcHeap).
//
// Arguments:
// vnk - Identifies whether to recurse to Conservative or Liberal value numbers
@@ -2478,7 +2479,7 @@ ValueNum ValueNumStore::VNApplySelectorsAssignTypeCoerce(ValueNum elem, var_type
// block - Block where the assignment occurs
//
// Return Value:
-// The value number corresopnding to the heap after the assignment.
+// The value number corresponding to memory after the assignment.
ValueNum ValueNumStore::VNApplySelectorsAssign(
ValueNumKind vnk, ValueNum map, FieldSeqNode* fieldSeq, ValueNum elem, var_types indType, BasicBlock* block)
@@ -2712,17 +2713,17 @@ ValueNum ValueNumStore::ExtendPtrVN(GenTreePtr opA, FieldSeqNode* fldSeq)
return res;
}
-void Compiler::fgValueNumberArrIndexAssign(CORINFO_CLASS_HANDLE elemTypeEq,
- ValueNum arrVN,
- ValueNum inxVN,
- FieldSeqNode* fldSeq,
- ValueNum rhsVN,
- var_types indType)
+ValueNum Compiler::fgValueNumberArrIndexAssign(CORINFO_CLASS_HANDLE elemTypeEq,
+ ValueNum arrVN,
+ ValueNum inxVN,
+ FieldSeqNode* fldSeq,
+ ValueNum rhsVN,
+ var_types indType)
{
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 @@ void 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,14 +2810,11 @@ void Compiler::fgValueNumberArrIndexAssign(CORINFO_CLASS_HANDLE elemTypeEq,
vnStore->vnDump(this, newValAtArrType);
printf("\n");
- 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);
-
- fgCurHeapVN = 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)
@@ -2869,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));
@@ -2923,6 +2922,17 @@ ValueNum Compiler::fgValueNumberArrIndexVal(GenTreePtr tree,
return selectedElem;
}
+ValueNum Compiler::fgValueNumberByrefExposedLoad(var_types type, ValueNum pointerVN)
+{
+ ValueNum memoryVN = fgCurMemoryVN[ByrefExposed];
+ // The memoization for VNFunc applications does not factor in the result type, so
+ // VNF_ByrefExposedLoad takes the loaded type as an explicit parameter.
+ ValueNum typeVN = vnStore->VNForIntCon(type);
+ ValueNum loadVN = vnStore->VNForFunc(type, VNF_ByrefExposedLoad, typeVN, vnStore->VNNormVal(pointerVN), memoryVN);
+
+ return loadVN;
+}
+
var_types ValueNumStore::TypeOfVN(ValueNum vn)
{
if (vn == NoVN)
@@ -4200,10 +4210,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)
{
@@ -4309,13 +4319,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
@@ -4329,7 +4339,7 @@ void Compiler::fgValueNumber()
while (vs.m_toDoAllPredsDone.Size() > 0)
{
BasicBlock* toDo = vs.m_toDoAllPredsDone.Pop();
- fgValueNumberBlock(toDo, /*newVNsForPhis*/ false);
+ fgValueNumberBlock(toDo);
// Record that we've visited "toDo", and add successors to the right sets.
vs.FinishVisit(toDo);
}
@@ -4344,7 +4354,7 @@ void Compiler::fgValueNumber()
continue; // We may have run out, because of completed blocks on the not-all-preds done list.
}
- fgValueNumberBlock(toDo, /*newVNsForPhis*/ true);
+ fgValueNumberBlock(toDo);
// Record that we've visited "toDo", and add successors to the right sest.
vs.FinishVisit(toDo);
}
@@ -4357,7 +4367,7 @@ void Compiler::fgValueNumber()
fgVNPassesCompleted++;
}
-void Compiler::fgValueNumberBlock(BasicBlock* blk, bool newVNsForPhis)
+void Compiler::fgValueNumberBlock(BasicBlock* blk)
{
compCurBB = blk;
@@ -4488,75 +4498,93 @@ void Compiler::fgValueNumberBlock(BasicBlock* blk, bool newVNsForPhis)
}
}
- // Now do the same for "Heap".
- // Is there a phi for this block?
- if (blk->bbHeapSsaPhiFunc == nullptr)
+ // Now do the same for each MemoryKind.
+ for (MemoryKind memoryKind : allMemoryKinds())
{
- fgCurHeapVN = GetHeapPerSsaData(blk->bbHeapSsaNumIn)->m_vnPair.GetLiberal();
- assert(fgCurHeapVN != ValueNumStore::NoVN);
- }
- else
- {
- unsigned loopNum;
- ValueNum newHeapVN;
- if (optBlockIsLoopEntry(blk, &loopNum))
+ // Is there a phi for this block?
+ if (blk->bbMemorySsaPhiFunc[memoryKind] == nullptr)
{
- newHeapVN = fgHeapVNForLoopSideEffects(blk, loopNum);
+ fgCurMemoryVN[memoryKind] = GetMemoryPerSsaData(blk->bbMemorySsaNumIn[memoryKind])->m_vnPair.GetLiberal();
+ assert(fgCurMemoryVN[memoryKind] != ValueNumStore::NoVN);
}
else
{
- // Are all the VN's the same?
- BasicBlock::HeapPhiArg* phiArgs = blk->bbHeapSsaPhiFunc;
- assert(phiArgs != BasicBlock::EmptyHeapPhiDef);
- // 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();
- if (sameVN == ValueNumStore::NoVN)
+ if ((memoryKind == ByrefExposed) && byrefStatesMatchGcHeapStates)
{
- allSame = false;
+ // The update for GcHeap will copy its result to ByrefExposed.
+ assert(memoryKind < GcHeap);
+ assert(blk->bbMemorySsaPhiFunc[memoryKind] == blk->bbMemorySsaPhiFunc[GcHeap]);
+ continue;
}
- phiArgs = phiArgs->m_nextArg;
- while (phiArgs != nullptr)
+
+ unsigned loopNum;
+ ValueNum newMemoryVN;
+ if (optBlockIsLoopEntry(blk, &loopNum))
{
- ValueNum phiArgVN = GetHeapPerSsaData(phiArgs->GetSsaNum())->m_vnPair.GetLiberal();
- if (phiArgVN == ValueNumStore::NoVN || phiArgVN != sameVN)
+ newMemoryVN = fgMemoryVNForLoopSideEffects(memoryKind, blk, loopNum);
+ }
+ else
+ {
+ // Are all the VN's the same?
+ BasicBlock::MemoryPhiArg* phiArgs = blk->bbMemorySsaPhiFunc[memoryKind];
+ 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 = GetMemoryPerSsaData(phiArgs->GetSsaNum())->m_vnPair.GetLiberal();
+ if (sameVN == ValueNumStore::NoVN)
{
allSame = false;
}
+ phiArgs = phiArgs->m_nextArg;
+ while (phiArgs != nullptr)
+ {
+ ValueNum phiArgVN = GetMemoryPerSsaData(phiArgs->GetSsaNum())->m_vnPair.GetLiberal();
+ if (phiArgVN == ValueNumStore::NoVN || phiArgVN != sameVN)
+ {
+ allSame = false;
+ }
#ifdef DEBUG
- ValueNum oldPhiAppVN = phiAppVN;
+ ValueNum oldPhiAppVN = phiAppVN;
#endif
- unsigned phiArgSSANum = phiArgs->GetSsaNum();
- ValueNum phiArgSSANumVN = vnStore->VNForIntCon(phiArgSSANum);
- JITDUMP(" Building phi application: $%x = SSA# %d.\n", phiArgSSANumVN, phiArgSSANum);
- phiAppVN = vnStore->VNForFunc(TYP_REF, VNF_Phi, phiArgSSANumVN, phiAppVN);
- JITDUMP(" Building phi application: $%x = phi($%x, $%x).\n", phiAppVN, phiArgSSANumVN, oldPhiAppVN);
- phiArgs = phiArgs->m_nextArg;
- }
- if (allSame)
- {
- newHeapVN = sameVN;
+ unsigned phiArgSSANum = phiArgs->GetSsaNum();
+ ValueNum phiArgSSANumVN = vnStore->VNForIntCon(phiArgSSANum);
+ JITDUMP(" Building phi application: $%x = SSA# %d.\n", phiArgSSANumVN, phiArgSSANum);
+ phiAppVN = vnStore->VNForFunc(TYP_REF, VNF_Phi, phiArgSSANumVN, phiAppVN);
+ JITDUMP(" Building phi application: $%x = phi($%x, $%x).\n", phiAppVN, phiArgSSANumVN,
+ oldPhiAppVN);
+ phiArgs = phiArgs->m_nextArg;
+ }
+ if (allSame)
+ {
+ newMemoryVN = sameVN;
+ }
+ else
+ {
+ newMemoryVN =
+ vnStore->VNForFunc(TYP_REF, VNF_PhiMemoryDef, vnStore->VNForHandle(ssize_t(blk), 0), phiAppVN);
+ }
}
- else
+ GetMemoryPerSsaData(blk->bbMemorySsaNumIn[memoryKind])->m_vnPair.SetLiberal(newMemoryVN);
+ fgCurMemoryVN[memoryKind] = newMemoryVN;
+ if ((memoryKind == GcHeap) && byrefStatesMatchGcHeapStates)
{
- newHeapVN =
- vnStore->VNForFunc(TYP_REF, VNF_PhiHeapDef, vnStore->VNForHandle(ssize_t(blk), 0), phiAppVN);
+ // Keep the CurMemoryVNs in sync
+ fgCurMemoryVN[ByrefExposed] = newMemoryVN;
}
}
- GetHeapPerSsaData(blk->bbHeapSsaNumIn)->m_vnPair.SetLiberal(newHeapVN);
- fgCurHeapVN = newHeapVN;
- }
#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("\n");
- }
+ if (verbose)
+ {
+ printf("The SSA definition for %s (#%d) at start of BB%02u is ", memoryKindNames[memoryKind],
+ blk->bbMemorySsaNumIn[memoryKind], blk->bbNum);
+ vnPrint(fgCurMemoryVN[memoryKind], 1);
+ printf("\n");
+ }
#endif // DEBUG
+ }
// Now iterate over the remaining statements, and their trees.
for (GenTreePtr stmt = firstNonPhi; stmt != nullptr; stmt = stmt->gtNext)
@@ -4592,15 +4620,30 @@ void Compiler::fgValueNumberBlock(BasicBlock* blk, bool newVNsForPhis)
#endif
}
- if (blk->bbHeapSsaNumOut != blk->bbHeapSsaNumIn)
+ for (MemoryKind memoryKind : allMemoryKinds())
{
- GetHeapPerSsaData(blk->bbHeapSsaNumOut)->m_vnPair.SetLiberal(fgCurHeapVN);
+ if ((memoryKind == GcHeap) && byrefStatesMatchGcHeapStates)
+ {
+ // The update to the shared SSA data will have already happened for ByrefExposed.
+ assert(memoryKind > ByrefExposed);
+ assert(blk->bbMemorySsaNumOut[memoryKind] == blk->bbMemorySsaNumOut[ByrefExposed]);
+ assert(GetMemoryPerSsaData(blk->bbMemorySsaNumOut[memoryKind])->m_vnPair.GetLiberal() ==
+ fgCurMemoryVN[memoryKind]);
+ continue;
+ }
+
+ if (blk->bbMemorySsaNumOut[memoryKind] != blk->bbMemorySsaNumIn[memoryKind])
+ {
+ GetMemoryPerSsaData(blk->bbMemorySsaNumOut[memoryKind])->m_vnPair.SetLiberal(fgCurMemoryVN[memoryKind]);
+ }
}
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);
@@ -4619,27 +4662,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)
@@ -4671,122 +4714,187 @@ 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);
+ }
}
}
+ else
+ {
+ // If there were any fields/elements modified, this should have been recorded as havoc
+ // for ByrefExposed.
+ assert(memoryKind == ByrefExposed);
+ assert((optLoopTable[loopNum].lpFieldsModified == nullptr) ||
+ optLoopTable[loopNum].lpLoopHasMemoryHavoc[memoryKind]);
+ assert((optLoopTable[loopNum].lpArrayElemTypesModified == nullptr) ||
+ optLoopTable[loopNum].lpLoopHasMemoryHavoc[memoryKind]);
+ }
#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::fgMutateGcHeap(GenTreePtr tree DEBUGARG(const char* 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::fgMutateHeap(GenTreePtr tree DEBUGARG(const char* msg))
+void Compiler::fgMutateAddressExposedLocal(GenTreePtr tree DEBUGARG(const char* msg))
{
- // bbHeapDef must be set to true for any block that Mutates the global Heap
- assert(compCurBB->bbHeapDef);
+ // Update the current ByrefExposed VN, and if we're tracking the heap SSA # caused by this node, record it.
+ recordAddressExposedLocalStore(tree, vnStore->VNForExpr(compCurBB) DEBUGARG(msg));
+}
- fgCurHeapVN = vnStore->VNForExpr(compCurBB, TYP_REF);
+void Compiler::recordGcHeapStore(GenTreePtr curTree, ValueNum gcHeapVN DEBUGARG(const char* msg))
+{
+ // bbMemoryDef must include GcHeap for any block that mutates the GC Heap
+ // and GC Heap mutations are also ByrefExposed mutations
+ assert((compCurBB->bbMemoryDef & memoryKindSet(GcHeap, ByrefExposed)) == memoryKindSet(GcHeap, ByrefExposed));
+ fgCurMemoryVN[GcHeap] = gcHeapVN;
- // If we're tracking the heap SSA # caused by this node, record it.
- fgValueNumberRecordHeapSsa(tree);
+ if (byrefStatesMatchGcHeapStates)
+ {
+ // Since GcHeap and ByrefExposed share SSA nodes, they need to share
+ // value numbers too.
+ fgCurMemoryVN[ByrefExposed] = gcHeapVN;
+ }
+ else
+ {
+ // GcHeap and ByrefExposed have different defnums and VNs. We conservatively
+ // assume that this GcHeap store may alias any byref load/store, so don't
+ // bother trying to record the map/select stuff, and instead just an opaque VN
+ // for ByrefExposed
+ fgCurMemoryVN[ByrefExposed] = vnStore->VNForExpr(compCurBB);
+ }
#ifdef DEBUG
if (verbose)
{
- printf(" fgCurHeapVN assigned by %s at ", msg);
- Compiler::printTreeID(tree);
- printf(" to new unique VN: " STR_VN "%x.\n", fgCurHeapVN);
+ printf(" fgCurMemoryVN[GcHeap] assigned by %s at ", msg);
+ Compiler::printTreeID(curTree);
+ printf(" to VN: " STR_VN "%x.\n", gcHeapVN);
}
#endif // DEBUG
+
+ // If byrefStatesMatchGcHeapStates is true, then since GcHeap and ByrefExposed share
+ // their SSA map entries, the below will effectively update both.
+ fgValueNumberRecordMemorySsa(GcHeap, curTree);
}
-void Compiler::fgValueNumberRecordHeapSsa(GenTreePtr tree)
+void Compiler::recordAddressExposedLocalStore(GenTreePtr curTree, ValueNum memoryVN DEBUGARG(const char* msg))
+{
+ // This should only happen if GcHeap and ByrefExposed are being tracked separately;
+ // otherwise we'd go through recordGcHeapStore.
+ assert(!byrefStatesMatchGcHeapStates);
+
+ // bbMemoryDef must include ByrefExposed for any block that mutates an address-exposed local
+ assert((compCurBB->bbMemoryDef & memoryKindSet(ByrefExposed)) != 0);
+ fgCurMemoryVN[ByrefExposed] = memoryVN;
+
+#ifdef DEBUG
+ if (verbose)
+ {
+ printf(" fgCurMemoryVN[ByrefExposed] assigned by %s at ", msg);
+ Compiler::printTreeID(curTree);
+ printf(" to VN: " STR_VN "%x.\n", memoryVN);
+ }
+#endif // DEBUG
+
+ fgValueNumberRecordMemorySsa(ByrefExposed, curTree);
+}
+
+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
@@ -4890,8 +4998,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())
@@ -4902,8 +5010,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();
@@ -4911,6 +5019,9 @@ void Compiler::fgValueNumberBlockAssignment(GenTreePtr tree, bool evalAsgLhsInd)
// SSA names in which to store VN's on defs. We'll yield unique VN's when we read from them.
if (!fgExcludeFromSsa(lclNum))
{
+ // Should not have been recorded as updating ByrefExposed.
+ assert(!GetMemorySsaMap(ByrefExposed)->Lookup(tree, &memorySsaNum));
+
unsigned lclDefSsaNum = GetSsaNumForLocalVarDef(lclVarTree);
ValueNum initBlkVN = ValueNumStore::NoVN;
@@ -4941,12 +5052,16 @@ void Compiler::fgValueNumberBlockAssignment(GenTreePtr tree, bool evalAsgLhsInd)
}
#endif // DEBUG
}
+ else if (lvaVarAddrExposed(lclVarTree->gtLclNum))
+ {
+ fgMutateAddressExposedLocal(tree DEBUGARG("INITBLK - address-exposed local"));
+ }
}
else
{
- // For now, arbitrary side effect on Heap.
+ // For now, arbitrary side effect on GcHeap/ByrefExposed.
// 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.
@@ -4956,7 +5071,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
@@ -4964,14 +5079,17 @@ 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;
// If it's excluded from SSA, don't need to do anything.
if (!fgExcludeFromSsa(lhsLclNum))
{
+ // Should not have been recorded as updating ByrefExposed.
+ assert(!GetMemorySsaMap(ByrefExposed)->Lookup(tree, &memorySsaNum));
+
unsigned lclDefSsaNum = GetSsaNumForLocalVarDef(lclVarTree);
if (lhs->IsLocalExpr(this, &lclVarTree, &lhsFldSeq) ||
@@ -5082,10 +5200,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 GcHeap (which is a subset of ByrefExposed).
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);
@@ -5162,12 +5280,16 @@ void Compiler::fgValueNumberBlockAssignment(GenTreePtr tree, bool evalAsgLhsInd)
}
#endif // DEBUG
}
+ else if (lvaVarAddrExposed(lhsLclNum))
+ {
+ fgMutateAddressExposedLocal(tree DEBUGARG("COPYBLK - address-exposed local"));
+ }
}
else
{
- // For now, arbitrary side effect on Heap.
+ // For now, arbitrary side effect on GcHeap/ByrefExposed.
// 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.
@@ -5223,8 +5345,22 @@ void Compiler::fgValueNumberTree(GenTreePtr tree, bool evalAsgLhsInd)
if (lcl->gtSsaNum == SsaConfig::RESERVED_SSA_NUM)
{
- // Not an SSA variable. Assign each occurrence a new, unique, VN.
- lcl->gtVNPair.SetBoth(vnStore->VNForExpr(compCurBB, lcl->TypeGet()));
+ // Not an SSA variable.
+
+ if (lvaVarAddrExposed(lclNum))
+ {
+ // Address-exposed locals are part of ByrefExposed.
+ ValueNum addrVN = vnStore->VNForFunc(TYP_BYREF, VNF_PtrToLoc, vnStore->VNForIntCon(lclNum),
+ vnStore->VNForFieldSeq(nullptr));
+ ValueNum loadVN = fgValueNumberByrefExposedLoad(typ, addrVN);
+
+ lcl->gtVNPair.SetBoth(loadVN);
+ }
+ else
+ {
+ // Assign odd cases a new, unique, VN.
+ lcl->gtVNPair.SetBoth(vnStore->VNForExpr(compCurBB, lcl->TypeGet()));
+ }
}
else
{
@@ -5366,11 +5502,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/ByrefExposed
+ 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/ByrefExposed if isVolatile is true, and then do the read as normal.
//
// This allows:
// 1: read s;
@@ -5399,13 +5535,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 GcHeap (which is a subset of ByrefExposed).
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);
@@ -5429,8 +5565,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/ByrefExposed.
+ fgMutateGcHeap(tree DEBUGARG("MEMORYBARRIER"));
break;
// These do not represent values.
@@ -5462,8 +5598,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))
@@ -5482,7 +5618,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))
{
@@ -5567,11 +5703,14 @@ 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)
{
+ // Should not have been recorded as updating ByrefExposed mem.
+ assert(!GetMemorySsaMap(ByrefExposed)->Lookup(tree, &memorySsaNum));
+
assert(rhsVNPair.GetLiberal() != ValueNumStore::NoVN);
lhs->gtVNPair = rhsVNPair;
@@ -5591,6 +5730,16 @@ void Compiler::fgValueNumberTree(GenTreePtr tree, bool evalAsgLhsInd)
}
#endif // DEBUG
}
+ else if (lvaVarAddrExposed(lcl->gtLclNum))
+ {
+ // We could use MapStore here and MapSelect on reads of address-exposed locals
+ // (using the local nums as selectors) to get e.g. propagation of values
+ // through address-taken locals in regions of code with no calls or byref
+ // writes.
+ // For now, just use a new opaque VN.
+ ValueNum heapVN = vnStore->VNForExpr(compCurBB);
+ recordAddressExposedLocalStore(tree, heapVN DEBUGARG("local assign"));
+ }
#ifdef DEBUG
else
{
@@ -5598,7 +5747,8 @@ void Compiler::fgValueNumberTree(GenTreePtr tree, bool evalAsgLhsInd)
{
JITDUMP("Tree ");
Compiler::printTreeID(tree);
- printf(" assigns to local var V%02u; excluded from SSA, so value not tracked.\n",
+ printf(" assigns to non-address-taken local var V%02u; excluded from SSA, so value not "
+ "tracked.\n",
lcl->GetLclNum());
}
}
@@ -5610,8 +5760,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)
{
@@ -5664,6 +5814,15 @@ void Compiler::fgValueNumberTree(GenTreePtr tree, bool evalAsgLhsInd)
}
#endif // DEBUG
}
+ else if (lvaVarAddrExposed(lclFld->gtLclNum))
+ {
+ // This side-effects ByrefExposed. Just use a new opaque VN.
+ // As with GT_LCL_VAR, we could probably use MapStore here and MapSelect at corresponding
+ // loads, but to do so would have to identify the subset of address-exposed locals
+ // whose fields can be disambiguated.
+ ValueNum heapVN = vnStore->VNForExpr(compCurBB);
+ recordAddressExposedLocalStore(tree, heapVN DEBUGARG("local field assign"));
+ }
}
break;
@@ -5678,8 +5837,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/ByrefExposed
+ fgMutateGcHeap(lhs DEBUGARG("GTF_IND_VOLATILE - store"));
tree->gtVNPair.SetBoth(vnStore->VNForExpr(compCurBB, lhs->TypeGet()));
}
@@ -5797,6 +5956,17 @@ void Compiler::fgValueNumberTree(GenTreePtr tree, bool evalAsgLhsInd)
}
#endif // DEBUG
}
+ else if (lvaVarAddrExposed(lclNum))
+ {
+ // Need to record the effect on ByrefExposed.
+ // We could use MapStore here and MapSelect on reads of address-exposed locals
+ // (using the local nums as selectors) to get e.g. propagation of values
+ // through address-taken locals in regions of code with no calls or byref
+ // writes.
+ // For now, just use a new opaque VN.
+ ValueNum heapVN = vnStore->VNForExpr(compCurBB);
+ recordAddressExposedLocalStore(tree, heapVN DEBUGARG("PtrToLoc indir"));
+ }
}
}
@@ -5832,9 +6002,9 @@ void Compiler::fgValueNumberTree(GenTreePtr tree, bool evalAsgLhsInd)
}
#endif // DEBUG
- fgValueNumberArrIndexAssign(elemTypeEq, arrVN, inxVN, fldSeq, rhsVNPair.GetLiberal(),
- lhs->TypeGet());
- fgValueNumberRecordHeapSsa(tree);
+ ValueNum heapVN = fgValueNumberArrIndexAssign(elemTypeEq, arrVN, inxVN, fldSeq,
+ rhsVNPair.GetLiberal(), lhs->TypeGet());
+ 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)
@@ -5851,7 +6021,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.
@@ -5869,15 +6039,15 @@ void Compiler::fgValueNumberTree(GenTreePtr tree, bool evalAsgLhsInd)
fldSeq = GetFieldSeqStore()->Append(fldSeq, zeroOffsetFldSeq);
}
- fgValueNumberArrIndexAssign(elemTypeEq, arrVN, inxVN, fldSeq, rhsVNPair.GetLiberal(),
- lhs->TypeGet());
- fgValueNumberRecordHeapSsa(tree);
+ ValueNum heapVN = fgValueNumberArrIndexAssign(elemTypeEq, arrVN, inxVN, fldSeq,
+ rhsVNPair.GetLiberal(), lhs->TypeGet());
+ 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
{
@@ -5892,8 +6062,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;
@@ -5906,7 +6076,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.
@@ -5962,8 +6133,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,
@@ -5973,26 +6144,47 @@ 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.
- fgCurHeapVN = vnStore->VNApplySelectorsAssign(VNK_Liberal, fgCurHeapVN, firstFieldOnly,
- newFldMapVN, indType, compCurBB);
+ // Update the field map for firstField in GcHeap to this new value.
+ ValueNum heapVN =
+ vnStore->VNApplySelectorsAssign(VNK_Liberal, fgCurMemoryVN[GcHeap], firstFieldOnly,
+ newFldMapVN, indType, compCurBB);
- fgValueNumberRecordHeapSsa(tree);
+ recordGcHeapStore(tree, heapVN DEBUGARG("StoreField"));
}
}
else
{
- GenTreeLclVarCommon* dummyLclVarTree = nullptr;
- if (!tree->DefinesLocal(this, &dummyLclVarTree))
+ GenTreeLclVarCommon* lclVarTree = nullptr;
+ bool isLocal = tree->DefinesLocal(this, &lclVarTree);
+
+ if (isLocal && lvaVarAddrExposed(lclVarTree->gtLclNum))
+ {
+ // Store to address-exposed local; need to record the effect on ByrefExposed.
+ // We could use MapStore here and MapSelect on reads of address-exposed locals
+ // (using the local nums as selectors) to get e.g. propagation of values
+ // through address-taken locals in regions of code with no calls or byref
+ // writes.
+ // For now, just use a new opaque VN.
+ ValueNum memoryVN = vnStore->VNForExpr(compCurBB);
+ recordAddressExposedLocalStore(tree, memoryVN DEBUGARG("PtrToLoc indir"));
+ }
+ else if (!isLocal)
{
- // 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/ByrefExposed.
+ // For the new ByrefExposed VN, we could use an operator here like
+ // VNF_ByrefExposedStore that carries the VNs of the pointer and RHS, then
+ // at byref loads if the current ByrefExposed VN happens to be
+ // VNF_ByrefExposedStore with the same pointer VN, we could propagate the
+ // VN from the RHS to the VN for the load. This would e.g. allow tracking
+ // values through assignments to out params. For now, just model this
+ // as an opaque GcHeap/ByrefExposed mutation.
+ fgMutateGcHeap(tree DEBUGARG("assign-of-IND"));
}
}
}
@@ -6008,17 +6200,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/ByrefExposed
+ 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 GcHeap (which is a subset of ByrefExposed).
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
@@ -6026,23 +6218,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
- fgCurHeapVN = storeVal;
- fgValueNumberRecordHeapSsa(tree);
+ // 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/ByrefExposed
+ fgMutateGcHeap(lhs DEBUGARG("Unkwown Assignment - store")); // always change fgCurMemoryVN
break;
}
}
@@ -6123,7 +6314,9 @@ void Compiler::fgValueNumberTree(GenTreePtr tree, bool evalAsgLhsInd)
else if ((oper == GT_IND) || GenTree::OperIsBlk(oper))
{
// So far, we handle cases in which the address is a ptr-to-local, or if it's
- // a pointer to an object field.
+ // a pointer to an object field or array alement. Other cases become uses of
+ // the current ByrefExposed value and the pointer value, so that at least we
+ // can recognize redundant loads with no stores between them.
GenTreePtr addr = tree->AsIndir()->Addr();
GenTreeLclVarCommon* lclVarTree = nullptr;
FieldSeqNode* fldSeq1 = nullptr;
@@ -6150,8 +6343,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/ByrefExposed
+ fgMutateGcHeap(tree DEBUGARG("GTF_IND_VOLATILE - read"));
// The value read by the GT_IND can immediately change
ValueNum newUniq = vnStore->VNForExpr(compCurBB, tree->TypeGet());
@@ -6226,6 +6419,39 @@ void Compiler::fgValueNumberTree(GenTreePtr tree, bool evalAsgLhsInd)
fgValueNumberArrIndexVal(tree, elemTypeEq, arrVN, inxVN, addrXvnp.GetLiberal(), fldSeq);
}
}
+ else if (tree->gtFlags & GTF_IND_ARR_LEN)
+ {
+ // It's an array length. The argument is the sum of an array ref with some integer values...
+ ValueNum arrRefLib = vnStore->VNForRefInAddr(tree->gtOp.gtOp1->gtVNPair.GetLiberal());
+ ValueNum arrRefCons = vnStore->VNForRefInAddr(tree->gtOp.gtOp1->gtVNPair.GetConservative());
+
+ assert(vnStore->TypeOfVN(arrRefLib) == TYP_REF || vnStore->TypeOfVN(arrRefLib) == TYP_BYREF);
+ if (vnStore->IsVNConstant(arrRefLib))
+ {
+ // (or in weird cases, a REF or BYREF constant, in which case the result is an exception).
+ tree->gtVNPair.SetLiberal(
+ vnStore->VNWithExc(ValueNumStore::VNForVoid(),
+ vnStore->VNExcSetSingleton(
+ vnStore->VNForFunc(TYP_REF, VNF_NullPtrExc, arrRefLib))));
+ }
+ else
+ {
+ tree->gtVNPair.SetLiberal(vnStore->VNForFunc(TYP_INT, VNFunc(GT_ARR_LENGTH), arrRefLib));
+ }
+ assert(vnStore->TypeOfVN(arrRefCons) == TYP_REF || vnStore->TypeOfVN(arrRefCons) == TYP_BYREF);
+ if (vnStore->IsVNConstant(arrRefCons))
+ {
+ // (or in weird cases, a REF or BYREF constant, in which case the result is an exception).
+ tree->gtVNPair.SetConservative(
+ vnStore->VNWithExc(ValueNumStore::VNForVoid(),
+ vnStore->VNExcSetSingleton(
+ vnStore->VNForFunc(TYP_REF, VNF_NullPtrExc, arrRefCons))));
+ }
+ else
+ {
+ tree->gtVNPair.SetConservative(vnStore->VNForFunc(TYP_INT, VNFunc(GT_ARR_LENGTH), arrRefCons));
+ }
+ }
// In general we skip GT_IND nodes on that are the LHS of an assignment. (We labeled these earlier.)
// We will "evaluate" this as part of the assignment. (Unless we're explicitly told by
@@ -6266,10 +6492,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 GcHeap (which is a subset of ByrefExposed).
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);
@@ -6294,7 +6520,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;
@@ -6313,7 +6539,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();
@@ -6357,9 +6583,12 @@ void Compiler::fgValueNumberTree(GenTreePtr tree, bool evalAsgLhsInd)
tree->gtVNPair = vnStore->VNPWithExc(tree->gtVNPair, addrXvnp);
}
}
- else // We don't know where the address points.
+ else // We don't know where the address points, so it is an ByrefExposed load.
{
- tree->gtVNPair.SetBoth(vnStore->VNForExpr(compCurBB, tree->TypeGet()));
+ ValueNum addrVN = addr->gtVNPair.GetLiberal();
+ ValueNum loadVN = fgValueNumberByrefExposedLoad(typ, addrVN);
+ tree->gtVNPair.SetLiberal(loadVN);
+ tree->gtVNPair.SetConservative(vnStore->VNForExpr(compCurBB, tree->TypeGet()));
tree->gtVNPair = vnStore->VNPWithExc(tree->gtVNPair, addrXvnp);
}
}
@@ -6506,62 +6735,16 @@ void Compiler::fgValueNumberTree(GenTreePtr tree, bool evalAsgLhsInd)
tree->gtOp.gtOp1->gtVNPair)));
break;
- case GT_BLK:
- case GT_OBJ:
- case GT_IND:
- if (tree->gtFlags & GTF_IND_ARR_LEN)
- {
- // It's an array length. The argument is the sum of an array ref with some integer values...
- ValueNum arrRefLib = vnStore->VNForRefInAddr(tree->gtOp.gtOp1->gtVNPair.GetLiberal());
- ValueNum arrRefCons = vnStore->VNForRefInAddr(tree->gtOp.gtOp1->gtVNPair.GetConservative());
-
- assert(vnStore->TypeOfVN(arrRefLib) == TYP_REF || vnStore->TypeOfVN(arrRefLib) == TYP_BYREF);
- if (vnStore->IsVNConstant(arrRefLib))
- {
- // (or in weird cases, a REF or BYREF constant, in which case the result is an exception).
- tree->gtVNPair.SetLiberal(
- vnStore->VNWithExc(ValueNumStore::VNForVoid(),
- vnStore->VNExcSetSingleton(
- vnStore->VNForFunc(TYP_REF, VNF_NullPtrExc, arrRefLib))));
- }
- else
- {
- tree->gtVNPair.SetLiberal(vnStore->VNForFunc(TYP_INT, VNFunc(GT_ARR_LENGTH), arrRefLib));
- }
- assert(vnStore->TypeOfVN(arrRefCons) == TYP_REF || vnStore->TypeOfVN(arrRefCons) == TYP_BYREF);
- if (vnStore->IsVNConstant(arrRefCons))
- {
- // (or in weird cases, a REF or BYREF constant, in which case the result is an exception).
- tree->gtVNPair.SetConservative(
- vnStore->VNWithExc(ValueNumStore::VNForVoid(),
- vnStore->VNExcSetSingleton(
- vnStore->VNForFunc(TYP_REF, VNF_NullPtrExc, arrRefCons))));
- }
- else
- {
- tree->gtVNPair.SetConservative(
- vnStore->VNForFunc(TYP_INT, VNFunc(GT_ARR_LENGTH), arrRefCons));
- }
- }
- else
- {
- tree->gtVNPair.SetBoth(vnStore->VNForExpr(compCurBB, tree->TypeGet()));
- }
- break;
-
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/ByrefExposed.
+ fgMutateGcHeap(tree DEBUGARG("Interlocked intrinsic"));
tree->gtVNPair.SetBoth(vnStore->VNForExpr(compCurBB, tree->TypeGet()));
break;
case GT_JTRUE:
case GT_LIST:
-#ifndef LEGACY_BACKEND
- case GT_FIELD_LIST:
-#endif // !LEGACY_BACKEND
// These nodes never need to have a ValueNumber
tree->gtVNPair.SetBoth(ValueNumStore::NoVN);
break;
@@ -6592,18 +6775,18 @@ void Compiler::fgValueNumberTree(GenTreePtr tree, bool evalAsgLhsInd)
// A bounds check node has no value, but may throw exceptions.
ValueNumPair excSet = vnStore->VNPExcSetSingleton(
vnStore->VNPairForFunc(TYP_REF, VNF_IndexOutOfRangeExc,
- vnStore->VNPNormVal(tree->AsBoundsChk()->gtArrLen->gtVNPair),
- vnStore->VNPNormVal(tree->AsBoundsChk()->gtIndex->gtVNPair)));
- excSet = vnStore->VNPExcSetUnion(excSet, vnStore->VNPExcVal(tree->AsBoundsChk()->gtArrLen->gtVNPair));
+ vnStore->VNPNormVal(tree->AsBoundsChk()->gtIndex->gtVNPair),
+ vnStore->VNPNormVal(tree->AsBoundsChk()->gtArrLen->gtVNPair)));
excSet = vnStore->VNPExcSetUnion(excSet, vnStore->VNPExcVal(tree->AsBoundsChk()->gtIndex->gtVNPair));
+ excSet = vnStore->VNPExcSetUnion(excSet, vnStore->VNPExcVal(tree->AsBoundsChk()->gtArrLen->gtVNPair));
tree->gtVNPair = vnStore->VNPWithExc(vnStore->VNPForVoid(), excSet);
}
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/ByrefExposed.
+ fgMutateGcHeap(tree DEBUGARG("Interlocked intrinsic"));
tree->gtVNPair.SetBoth(vnStore->VNForExpr(compCurBB, tree->TypeGet()));
break;
@@ -6896,6 +7079,24 @@ void Compiler::fgValueNumberHelperCallFunc(GenTreeCall* call, VNFunc vnf, ValueN
}
else
{
+ auto getCurrentArg = [call, &args, useEntryPointAddrAsArg0](int currentIndex) {
+ GenTreePtr arg = args->Current();
+ if ((arg->gtFlags & GTF_LATE_ARG) != 0)
+ {
+ // This arg is a setup node that moves the arg into position.
+ // Value-numbering will have visited the separate late arg that
+ // holds the actual value, and propagated/computed the value number
+ // for this arg there.
+ if (useEntryPointAddrAsArg0)
+ {
+ // The args in the fgArgInfo don't include the entry point, so
+ // index into them using one less than the requested index.
+ --currentIndex;
+ }
+ return call->fgArgInfo->GetLateArg(currentIndex);
+ }
+ return arg;
+ };
// Has at least one argument.
ValueNumPair vnp0;
ValueNumPair vnp0x = ValueNumStore::VNPForEmptyExcSet();
@@ -6909,7 +7110,7 @@ void Compiler::fgValueNumberHelperCallFunc(GenTreeCall* call, VNFunc vnf, ValueN
#endif
{
assert(!useEntryPointAddrAsArg0);
- ValueNumPair vnp0wx = args->Current()->gtVNPair;
+ ValueNumPair vnp0wx = getCurrentArg(0)->gtVNPair;
vnStore->VNPUnpackExc(vnp0wx, &vnp0, &vnp0x);
// Also include in the argument exception sets
@@ -6931,7 +7132,7 @@ void Compiler::fgValueNumberHelperCallFunc(GenTreeCall* call, VNFunc vnf, ValueN
else
{
// Has at least two arguments.
- ValueNumPair vnp1wx = args->Current()->gtVNPair;
+ ValueNumPair vnp1wx = getCurrentArg(1)->gtVNPair;
ValueNumPair vnp1;
ValueNumPair vnp1x = ValueNumStore::VNPForEmptyExcSet();
vnStore->VNPUnpackExc(vnp1wx, &vnp1, &vnp1x);
@@ -6951,7 +7152,7 @@ void Compiler::fgValueNumberHelperCallFunc(GenTreeCall* call, VNFunc vnf, ValueN
}
else
{
- ValueNumPair vnp2wx = args->Current()->gtVNPair;
+ ValueNumPair vnp2wx = getCurrentArg(2)->gtVNPair;
ValueNumPair vnp2;
ValueNumPair vnp2x = ValueNumStore::VNPForEmptyExcSet();
vnStore->VNPUnpackExc(vnp2wx, &vnp2, &vnp2x);
@@ -6988,16 +7189,7 @@ void Compiler::fgValueNumberCall(GenTreeCall* call)
if (arg->OperGet() == GT_ARGPLACE)
{
// Find the corresponding late arg.
- GenTreePtr lateArg = nullptr;
- for (unsigned j = 0; j < call->fgArgInfo->ArgCount(); j++)
- {
- if (call->fgArgInfo->ArgTable()[j]->argNum == i)
- {
- lateArg = call->fgArgInfo->ArgTable()[j]->node;
- break;
- }
- }
- assert(lateArg != nullptr);
+ GenTreePtr lateArg = call->fgArgInfo->GetLateArg(i);
assert(lateArg->gtVNPair.BothDefined());
arg->gtVNPair = lateArg->gtVNPair;
updatedArgPlace = true;
@@ -7028,8 +7220,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/ByrefExposed.
+ fgMutateGcHeap(call DEBUGARG("HELPER - modifies heap"));
}
}
else
@@ -7043,8 +7235,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/ByrefExposed.
+ fgMutateGcHeap(call DEBUGARG("CALL"));
}
}