diff options
Diffstat (limited to 'src/jit/valuenum.cpp')
-rw-r--r-- | src/jit/valuenum.cpp | 804 |
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")); } } |