From 835f679cdad940e7ccf9aef7e19932ddef631ed7 Mon Sep 17 00:00:00 2001 From: Mike Danes Date: Sun, 2 Dec 2018 15:47:50 +0200 Subject: [PATCH] Pull struct type info out of GenTreeObj --- src/jit/codegenarm.cpp | 37 ++--- src/jit/codegenarm64.cpp | 26 ++- src/jit/codegenarmarch.cpp | 52 +++--- src/jit/codegencommon.cpp | 25 +-- src/jit/codegenlinear.cpp | 1 - src/jit/codegenxarch.cpp | 54 +++---- src/jit/compiler.cpp | 131 +++++++-------- src/jit/compiler.h | 32 ++-- src/jit/compiler.hpp | 11 +- src/jit/gcencode.cpp | 34 ++-- src/jit/gcinfo.cpp | 14 +- src/jit/gentree.cpp | 96 +++-------- src/jit/gentree.h | 320 ++++++++++++++++++++++++------------- src/jit/gschecks.cpp | 2 +- src/jit/importer.cpp | 56 +------ src/jit/jit.h | 2 +- src/jit/jithashtable.h | 2 + src/jit/lclvars.cpp | 79 ++------- src/jit/lower.cpp | 29 +--- src/jit/lowerarmarch.cpp | 14 +- src/jit/lowerxarch.cpp | 19 +-- src/jit/lsraarmarch.cpp | 2 +- src/jit/lsrabuild.cpp | 4 +- src/jit/lsraxarch.cpp | 2 +- src/jit/morph.cpp | 50 +++--- src/jit/rationalize.cpp | 40 ++--- src/jit/simd.cpp | 15 +- 27 files changed, 523 insertions(+), 626 deletions(-) diff --git a/src/jit/codegenarm.cpp b/src/jit/codegenarm.cpp index 21267a69c61d..5dfd405b90f5 100644 --- a/src/jit/codegenarm.cpp +++ b/src/jit/codegenarm.cpp @@ -712,7 +712,7 @@ void CodeGen::genCodeForCpObj(GenTreeObj* cpObjNode) // This GenTree node has data about GC pointers, this means we're dealing // with CpObj. - assert(cpObjNode->gtGcPtrCount > 0); + assert(cpObjNode->gtLayout->HasGCPtr()); #endif // DEBUG // Consume the operands and get them into the right registers. @@ -731,29 +731,18 @@ void CodeGen::genCodeForCpObj(GenTreeObj* cpObjNode) instGen_MemoryBarrier(); } - unsigned slots = cpObjNode->gtSlots; + unsigned slots = cpObjNode->gtLayout->GetSlotCount(); emitter* emit = getEmitter(); - BYTE* gcPtrs = cpObjNode->gtGcPtrs; + StructTypeLayout* layout = cpObjNode->gtLayout; // If we can prove it's on the stack we don't need to use the write barrier. - emitAttr attr = EA_PTRSIZE; if (dstOnStack) { for (unsigned i = 0; i < slots; ++i) { - if (gcPtrs[i] == GCT_GCREF) - { - attr = EA_GCREF; - } - else if (gcPtrs[i] == GCT_BYREF) - { - attr = EA_BYREF; - } - else - { - attr = EA_PTRSIZE; - } + var_types type = Compiler::getJitGCType(layout->GetGCPtr(i)); + emitAttr attr = emitTypeSize(type); emit->emitIns_R_R_I(INS_ldr, attr, tmpReg, REG_WRITE_BARRIER_SRC_BYREF, TARGET_POINTER_SIZE, INS_FLAGS_DONT_CARE, INS_OPTS_LDST_POST_INC); @@ -763,30 +752,28 @@ void CodeGen::genCodeForCpObj(GenTreeObj* cpObjNode) } else { - unsigned gcPtrCount = cpObjNode->gtGcPtrCount; - - unsigned i = 0; + unsigned gcPtrCount = 0; + unsigned i = 0; while (i < slots) { - switch (gcPtrs[i]) + switch (layout->GetGCPtr(i)) { case TYPE_GC_NONE: - emit->emitIns_R_R_I(INS_ldr, attr, tmpReg, REG_WRITE_BARRIER_SRC_BYREF, TARGET_POINTER_SIZE, + emit->emitIns_R_R_I(INS_ldr, EA_PTRSIZE, tmpReg, REG_WRITE_BARRIER_SRC_BYREF, TARGET_POINTER_SIZE, INS_FLAGS_DONT_CARE, INS_OPTS_LDST_POST_INC); - emit->emitIns_R_R_I(INS_str, attr, tmpReg, REG_WRITE_BARRIER_DST_BYREF, TARGET_POINTER_SIZE, + emit->emitIns_R_R_I(INS_str, EA_PTRSIZE, tmpReg, REG_WRITE_BARRIER_DST_BYREF, TARGET_POINTER_SIZE, INS_FLAGS_DONT_CARE, INS_OPTS_LDST_POST_INC); break; default: // In the case of a GC-Pointer we'll call the ByRef write barrier helper genEmitHelperCall(CORINFO_HELP_ASSIGN_BYREF, 0, EA_PTRSIZE); - - gcPtrCount--; + gcPtrCount++; break; } ++i; } - assert(gcPtrCount == 0); + assert(gcPtrCount == cpObjNode->gtLayout->GetGCPtrCount()); } if (cpObjNode->gtFlags & GTF_BLK_VOLATILE) diff --git a/src/jit/codegenarm64.cpp b/src/jit/codegenarm64.cpp index bd827ec8381a..a6b5a95bbfbd 100644 --- a/src/jit/codegenarm64.cpp +++ b/src/jit/codegenarm64.cpp @@ -2457,7 +2457,7 @@ void CodeGen::genCodeForCpObj(GenTreeObj* cpObjNode) // This GenTree node has data about GC pointers, this means we're dealing // with CpObj. - assert(cpObjNode->gtGcPtrCount > 0); + assert(cpObjNode->gtLayout->HasGCPtr()); #endif // DEBUG // Consume the operands and get them into the right registers. @@ -2466,7 +2466,7 @@ void CodeGen::genCodeForCpObj(GenTreeObj* cpObjNode) gcInfo.gcMarkRegPtrVal(REG_WRITE_BARRIER_SRC_BYREF, srcAddrType); gcInfo.gcMarkRegPtrVal(REG_WRITE_BARRIER_DST_BYREF, dstAddr->TypeGet()); - unsigned slots = cpObjNode->gtSlots; + unsigned slots = cpObjNode->gtLayout->GetSlotCount(); // Temp register(s) used to perform the sequence of loads and stores. regNumber tmpReg = cpObjNode->ExtractTempReg(); @@ -2493,7 +2493,7 @@ void CodeGen::genCodeForCpObj(GenTreeObj* cpObjNode) emitter* emit = getEmitter(); - BYTE* gcPtrs = cpObjNode->gtGcPtrs; + StructTypeLayout* layout = cpObjNode->gtLayout; // If we can prove it's on the stack we don't need to use the write barrier. if (dstOnStack) @@ -2502,8 +2502,8 @@ void CodeGen::genCodeForCpObj(GenTreeObj* cpObjNode) // Check if two or more remaining slots and use a ldp/stp sequence while (i < slots - 1) { - emitAttr attr0 = emitTypeSize(compiler->getJitGCType(gcPtrs[i + 0])); - emitAttr attr1 = emitTypeSize(compiler->getJitGCType(gcPtrs[i + 1])); + emitAttr attr0 = emitTypeSize(compiler->getJitGCType(layout->GetGCPtr(i + 0))); + emitAttr attr1 = emitTypeSize(compiler->getJitGCType(layout->GetGCPtr(i + 1))); emit->emitIns_R_R_R_I(INS_ldp, attr0, tmpReg, tmpReg2, REG_WRITE_BARRIER_SRC_BYREF, 2 * TARGET_POINTER_SIZE, INS_OPTS_POST_INDEX, attr1); @@ -2515,7 +2515,7 @@ void CodeGen::genCodeForCpObj(GenTreeObj* cpObjNode) // Use a ldr/str sequence for the last remainder if (i < slots) { - emitAttr attr0 = emitTypeSize(compiler->getJitGCType(gcPtrs[i + 0])); + emitAttr attr0 = emitTypeSize(compiler->getJitGCType(layout->GetGCPtr(i + 0))); emit->emitIns_R_R_I(INS_ldr, attr0, tmpReg, REG_WRITE_BARRIER_SRC_BYREF, TARGET_POINTER_SIZE, INS_OPTS_POST_INDEX); @@ -2525,16 +2525,15 @@ void CodeGen::genCodeForCpObj(GenTreeObj* cpObjNode) } else { - unsigned gcPtrCount = cpObjNode->gtGcPtrCount; - - unsigned i = 0; + unsigned gcPtrCount = 0; + unsigned i = 0; while (i < slots) { - switch (gcPtrs[i]) + switch (layout->GetGCPtr(i)) { case TYPE_GC_NONE: // Check if the next slot's type is also TYP_GC_NONE and use ldp/stp - if ((i + 1 < slots) && (gcPtrs[i + 1] == TYPE_GC_NONE)) + if ((i + 1 < slots) && (layout->GetGCPtr(i + 1) == TYPE_GC_NONE)) { emit->emitIns_R_R_R_I(INS_ldp, EA_8BYTE, tmpReg, tmpReg2, REG_WRITE_BARRIER_SRC_BYREF, 2 * TARGET_POINTER_SIZE, INS_OPTS_POST_INDEX); @@ -2554,13 +2553,12 @@ void CodeGen::genCodeForCpObj(GenTreeObj* cpObjNode) default: // In the case of a GC-Pointer we'll call the ByRef write barrier helper genEmitHelperCall(CORINFO_HELP_ASSIGN_BYREF, 0, EA_PTRSIZE); - - gcPtrCount--; + gcPtrCount++; break; } ++i; } - assert(gcPtrCount == 0); + assert(gcPtrCount == cpObjNode->gtLayout->GetGCPtrCount()); } if (cpObjNode->gtFlags & GTF_BLK_VOLATILE) diff --git a/src/jit/codegenarmarch.cpp b/src/jit/codegenarmarch.cpp index 494234b8ea19..8fc01a23f2ae 100644 --- a/src/jit/codegenarmarch.cpp +++ b/src/jit/codegenarmarch.cpp @@ -701,12 +701,9 @@ void CodeGen::genPutArgStk(GenTreePutArgStk* treeNode) // the xor ensures that only one of the two is setup, not both assert((varNode != nullptr) ^ (addrNode != nullptr)); - BYTE gcPtrArray[MAX_ARG_REG_COUNT] = {}; // TYPE_GC_NONE = 0 - BYTE* gcPtrs = gcPtrArray; - - unsigned gcPtrCount; // The count of GC pointers in the struct - int structSize; - bool isHfa; + StructTypeLayout* layout = nullptr; + int structSize; + bool isHfa; // This is the varNum for our load operations, // only used when we have a multireg struct with a LclVar source @@ -714,8 +711,7 @@ void CodeGen::genPutArgStk(GenTreePutArgStk* treeNode) #ifdef _TARGET_ARM_ // On ARM32, size of reference map can be larger than MAX_ARG_REG_COUNT - gcPtrs = treeNode->gtGcPtrs; - gcPtrCount = treeNode->gtNumberReferenceSlots; + layout = treeNode->gtLayout; #endif // Setup the structSize, isHFa, and gcPtrCount if (varNode != nullptr) @@ -733,9 +729,7 @@ void CodeGen::genPutArgStk(GenTreePutArgStk* treeNode) // as that is how much stack is allocated for this LclVar isHfa = varDsc->lvIsHfa(); #ifdef _TARGET_ARM64_ - gcPtrCount = varDsc->lvStructGcCount; - for (unsigned i = 0; i < gcPtrCount; ++i) - gcPtrs[i] = varDsc->lvGcLayout[i]; + layout = varDsc->lvLayout; #endif // _TARGET_ARM_ } else // addrNode is used @@ -756,12 +750,12 @@ void CodeGen::genPutArgStk(GenTreePutArgStk* treeNode) } #endif // _TARGET_ARM64_ - CORINFO_CLASS_HANDLE objClass = source->gtObj.gtClass; + CORINFO_CLASS_HANDLE objClass = source->AsObj()->gtLayout->GetClass(); - structSize = compiler->info.compCompHnd->getClassSize(objClass); + structSize = source->AsObj()->gtLayout->GetSize(); isHfa = compiler->IsHfa(objClass); #ifdef _TARGET_ARM64_ - gcPtrCount = compiler->info.compCompHnd->getClassGClayout(objClass, &gcPtrs[0]); + layout = source->AsObj()->gtLayout; #endif } @@ -769,7 +763,7 @@ void CodeGen::genPutArgStk(GenTreePutArgStk* treeNode) // if not then the max size for the the struct is 16 bytes if (isHfa) { - noway_assert(gcPtrCount == 0); + noway_assert(!layout->HasGCPtr()); } #ifdef _TARGET_ARM64_ else @@ -791,8 +785,8 @@ void CodeGen::genPutArgStk(GenTreePutArgStk* treeNode) while (remainingSize >= 2 * TARGET_POINTER_SIZE) { - var_types type0 = compiler->getJitGCType(gcPtrs[nextIndex + 0]); - var_types type1 = compiler->getJitGCType(gcPtrs[nextIndex + 1]); + var_types type0 = Compiler::getJitGCType(layout->GetGCPtr(nextIndex + 0)); + var_types type1 = Compiler::getJitGCType(layout->GetGCPtr(nextIndex + 1)); if (varNode != nullptr) { @@ -827,7 +821,7 @@ void CodeGen::genPutArgStk(GenTreePutArgStk* treeNode) // str r2, [sp, #16] while (remainingSize >= TARGET_POINTER_SIZE) { - var_types type = compiler->getJitGCType(gcPtrs[nextIndex]); + var_types type = Compiler::getJitGCType(layout->GetGCPtr(nextIndex)); if (varNode != nullptr) { @@ -864,7 +858,7 @@ void CodeGen::genPutArgStk(GenTreePutArgStk* treeNode) { if (remainingSize >= TARGET_POINTER_SIZE) { - var_types nextType = compiler->getJitGCType(gcPtrs[nextIndex]); + var_types nextType = Compiler::getJitGCType(layout->GetGCPtr(nextIndex)); emitAttr nextAttr = emitTypeSize(nextType); remainingSize -= TARGET_POINTER_SIZE; @@ -897,7 +891,7 @@ void CodeGen::genPutArgStk(GenTreePutArgStk* treeNode) assert(varNode == nullptr); // the left over size is smaller than a pointer and thus can never be a GC type - assert(varTypeIsGC(compiler->getJitGCType(gcPtrs[nextIndex])) == false); + assert(varTypeIsGC(Compiler::getJitGCType(layout->GetGCPtr(nextIndex))) == false); var_types loadType = TYP_UINT; if (loadSize == 1) @@ -1071,9 +1065,9 @@ void CodeGen::genPutArgSplit(GenTreePutArgSplit* treeNode) assert((varNode != nullptr) ^ (addrNode != nullptr)); // Setup the structSize, isHFa, and gcPtrCount - BYTE* gcPtrs = treeNode->gtGcPtrs; - unsigned gcPtrCount = treeNode->gtNumberReferenceSlots; // The count of GC pointers in the struct - int structSize = treeNode->getArgSize(); + StructTypeLayout* layout = treeNode->gtLayout; + unsigned gcPtrCount = treeNode->gtNumberReferenceSlots; // The count of GC pointers in the struct + int structSize = treeNode->getArgSize(); // This is the varNum for our load operations, // only used when we have a struct with a LclVar source @@ -1109,7 +1103,7 @@ void CodeGen::genPutArgSplit(GenTreePutArgSplit* treeNode) assert(baseReg != addrReg); // We don't split HFA struct - assert(!compiler->IsHfa(source->gtObj.gtClass)); + assert(!compiler->IsHfa(source->AsObj()->gtLayout->GetClass())); } // Put on stack first @@ -1121,7 +1115,7 @@ void CodeGen::genPutArgSplit(GenTreePutArgSplit* treeNode) assert(remainingSize % TARGET_POINTER_SIZE == 0); while (remainingSize > 0) { - var_types type = compiler->getJitGCType(gcPtrs[nextIndex]); + var_types type = Compiler::getJitGCType(layout->GetGCPtr(nextIndex)); if (varNode != nullptr) { @@ -2674,7 +2668,7 @@ void CodeGen::genJmpMethod(GenTree* jmp) { // Must be <= 16 bytes or else it wouldn't be passed in registers noway_assert(EA_SIZE_IN_BYTES(varDsc->lvSize()) <= MAX_PASS_MULTIREG_BYTES); - loadType = compiler->getJitGCType(varDsc->lvGcLayout[0]); + loadType = Compiler::getJitGCType(varDsc->lvLayout->GetGCPtr(0)); } else { @@ -2700,7 +2694,7 @@ void CodeGen::genJmpMethod(GenTree* jmp) // Restore the second register. argRegNext = genRegArgNext(argReg); - loadType = compiler->getJitGCType(varDsc->lvGcLayout[1]); + loadType = Compiler::getJitGCType(varDsc->lvLayout->GetGCPtr(1)); loadSize = emitActualTypeSize(loadType); getEmitter()->emitIns_R_S(ins_Load(loadType), loadSize, argRegNext, varNum, TARGET_POINTER_SIZE); @@ -2789,7 +2783,7 @@ void CodeGen::genJmpMethod(GenTree* jmp) for (unsigned ofs = 0; ofs < maxSize; ofs += REGSIZE_BYTES) { unsigned idx = ofs / REGSIZE_BYTES; - loadType = compiler->getJitGCType(varDsc->lvGcLayout[idx]); + loadType = Compiler::getJitGCType(varDsc->lvLayout->GetGCPtr(idx)); if (varDsc->lvRegNum != argReg) { @@ -3394,7 +3388,7 @@ void CodeGen::genCodeForStoreBlk(GenTreeBlk* blkOp) if (blkOp->OperIs(GT_STORE_OBJ) && blkOp->OperIsCopyBlkOp()) { - assert(blkOp->AsObj()->gtGcPtrCount != 0); + assert(blkOp->AsObj()->gtLayout->HasGCPtr()); genCodeForCpObj(blkOp->AsObj()); return; } diff --git a/src/jit/codegencommon.cpp b/src/jit/codegencommon.cpp index 87a693c337d8..3821ac9cc9ac 100644 --- a/src/jit/codegencommon.cpp +++ b/src/jit/codegencommon.cpp @@ -1141,8 +1141,8 @@ unsigned CodeGenInterface::InferStructOpSizeAlign(GenTree* op, unsigned* alignme if (op->gtOper == GT_OBJ) { - CORINFO_CLASS_HANDLE clsHnd = op->AsObj()->gtClass; - opSize = compiler->info.compCompHnd->getClassSize(clsHnd); + CORINFO_CLASS_HANDLE clsHnd = op->AsObj()->gtLayout->GetClass(); + opSize = op->AsObj()->gtLayout->GetSize(); alignment = roundUp(compiler->info.compCompHnd->getClassAlignmentRequirement(clsHnd), TARGET_POINTER_SIZE); } else if (op->gtOper == GT_LCL_VAR) @@ -4704,8 +4704,7 @@ void CodeGen::genCheckUseBlockInit() continue; } - if (compiler->info.compInitMem || varTypeIsGC(varDsc->TypeGet()) || (varDsc->lvStructGcCount > 0) || - varDsc->lvMustInit) + if (compiler->info.compInitMem || compiler->lvaTypeIsGC(varNum) || varDsc->lvMustInit) { if (varDsc->lvTracked) { @@ -4756,8 +4755,7 @@ void CodeGen::genCheckUseBlockInit() #else // !FEATURE_SIMD if ((!varDsc->lvTracked || (varDsc->lvType == TYP_STRUCT)) && #endif // !FEATURE_SIMD - varDsc->lvOnFrame && - (!varDsc->lvIsTemp || varTypeIsGC(varDsc->TypeGet()) || (varDsc->lvStructGcCount > 0))) + varDsc->lvOnFrame && (!varDsc->lvIsTemp || compiler->lvaTypeIsGC(varNum))) { varDsc->lvMustInit = true; @@ -4792,7 +4790,14 @@ void CodeGen::genCheckUseBlockInit() if (varDsc->lvMustInit && varDsc->lvOnFrame) { - initStkLclCnt += varDsc->lvStructGcCount; + if (varDsc->TypeGet() == TYP_STRUCT) + { + initStkLclCnt += varDsc->lvLayout->GetGCPtrCount(); + } + else if (varTypeIsGC(varDsc->TypeGet())) + { + initStkLclCnt += 1; + } } if ((compiler->lvaLclSize(varNum) > (3 * TARGET_POINTER_SIZE)) && (largeGcStructs <= 4)) @@ -6244,12 +6249,12 @@ void CodeGen::genZeroInitFrame(int untrLclHi, int untrLclLo, regNumber initReg, (varDsc->lvExactSize >= TARGET_POINTER_SIZE)) { // We only initialize the GC variables in the TYP_STRUCT - const unsigned slots = (unsigned)compiler->lvaLclSize(varNum) / REGSIZE_BYTES; - const BYTE* gcPtrs = compiler->lvaGetGcLayout(varNum); + StructTypeLayout* layout = varDsc->lvLayout; + const unsigned slots = layout->GetSlotCount(); for (unsigned i = 0; i < slots; i++) { - if (gcPtrs[i] != TYPE_GC_NONE) + if (layout->GetGCPtr(i) != TYPE_GC_NONE) { getEmitter()->emitIns_S_R(ins_Store(TYP_I_IMPL), EA_PTRSIZE, genGetZeroReg(initReg, pInitRegZeroed), varNum, i * REGSIZE_BYTES); diff --git a/src/jit/codegenlinear.cpp b/src/jit/codegenlinear.cpp index 7da189658935..7723692859ac 100644 --- a/src/jit/codegenlinear.cpp +++ b/src/jit/codegenlinear.cpp @@ -1027,7 +1027,6 @@ void CodeGen::genUnspillRegIfNeeded(GenTree* tree) unsigned flags = splitArg->GetRegSpillFlagByIdx(i); if ((flags & GTF_SPILLED) != 0) { - BYTE* gcPtrs = splitArg->gtGcPtrs; var_types dstType = splitArg->GetRegType(i); regNumber dstReg = splitArg->GetRegNumByIdx(i); diff --git a/src/jit/codegenxarch.cpp b/src/jit/codegenxarch.cpp index cf9055c31d6d..61da3481c834 100644 --- a/src/jit/codegenxarch.cpp +++ b/src/jit/codegenxarch.cpp @@ -2718,7 +2718,7 @@ void CodeGen::genCodeForStoreBlk(GenTreeBlk* storeBlkNode) if (storeBlkNode->OperIs(GT_STORE_OBJ) && storeBlkNode->OperIsCopyBlkOp() && !storeBlkNode->gtBlkOpGcUnsafe) { - assert(storeBlkNode->AsObj()->gtGcPtrCount != 0); + assert(storeBlkNode->AsObj()->gtLayout->HasGCPtr()); genCodeForCpObj(storeBlkNode->AsObj()); return; } @@ -3513,7 +3513,7 @@ void CodeGen::genCodeForCpObj(GenTreeObj* cpObjNode) // If the GenTree node has data about GC pointers, this means we're dealing // with CpObj, so this requires special logic. - assert(cpObjNode->gtGcPtrCount > 0); + assert(cpObjNode->gtLayout->HasGCPtr()); // MovSp (alias for movsq on x64 and movsd on x86) instruction is used for copying non-gcref fields // and it needs src = RSI and dst = RDI. @@ -3551,7 +3551,7 @@ void CodeGen::genCodeForCpObj(GenTreeObj* cpObjNode) gcInfo.gcMarkRegPtrVal(REG_RSI, srcAddrType); gcInfo.gcMarkRegPtrVal(REG_RDI, dstAddr->TypeGet()); - unsigned slots = cpObjNode->gtSlots; + unsigned slots = cpObjNode->gtLayout->GetSlotCount(); // If we can prove it's on the stack we don't need to use the write barrier. if (dstOnStack) @@ -3578,13 +3578,13 @@ void CodeGen::genCodeForCpObj(GenTreeObj* cpObjNode) } else { - BYTE* gcPtrs = cpObjNode->gtGcPtrs; - unsigned gcPtrCount = cpObjNode->gtGcPtrCount; + StructTypeLayout* layout = cpObjNode->gtLayout; + unsigned gcPtrCount = 0; unsigned i = 0; while (i < slots) { - switch (gcPtrs[i]) + switch (layout->GetGCPtr(i)) { case TYPE_GC_NONE: // Let's see if we can use rep movsp instead of a sequence of movsp instructions @@ -3596,7 +3596,7 @@ void CodeGen::genCodeForCpObj(GenTreeObj* cpObjNode) { nonGcSlotCount++; i++; - } while (i < slots && gcPtrs[i] == TYPE_GC_NONE); + } while (i < slots && layout->GetGCPtr(i) == TYPE_GC_NONE); // If we have a very small contiguous non-gc region, it's better just to // emit a sequence of movsp instructions @@ -3622,12 +3622,12 @@ void CodeGen::genCodeForCpObj(GenTreeObj* cpObjNode) default: // We have a GC pointer, call the memory barrier. genEmitHelperCall(CORINFO_HELP_ASSIGN_BYREF, 0, EA_PTRSIZE); - gcPtrCount--; + gcPtrCount++; i++; } } - assert(gcPtrCount == 0); + assert(gcPtrCount == layout->GetGCPtrCount()); } // Clear the gcInfo for RSI and RDI. @@ -5302,7 +5302,7 @@ void CodeGen::genCallInstruction(GenTreeCall* call) if (source->TypeGet() == TYP_STRUCT) { GenTreeObj* obj = source->AsObj(); - unsigned argBytes = roundUp(obj->gtBlkSize, TARGET_POINTER_SIZE); + unsigned argBytes = roundUp(obj->Size(), TARGET_POINTER_SIZE); assert((curArgTabEntry->numSlots * TARGET_POINTER_SIZE) == argBytes); } #endif // FEATURE_PUT_STRUCT_ARG_STK @@ -8304,9 +8304,9 @@ void CodeGen::genPutStructArgStk(GenTreePutArgStk* putArgStk) // future. assert(m_pushStkArg); - GenTree* srcAddr = source->gtGetOp1(); - BYTE* gcPtrs = putArgStk->gtGcPtrs; - const unsigned numSlots = putArgStk->gtNumSlots; + GenTree* srcAddr = source->gtGetOp1(); + StructTypeLayout* layout = putArgStk->gtLayout; + const unsigned numSlots = putArgStk->gtNumSlots; regNumber srcRegNum = srcAddr->gtRegNum; const bool srcAddrInReg = srcRegNum != REG_NA; @@ -8330,20 +8330,8 @@ void CodeGen::genPutStructArgStk(GenTreePutArgStk* putArgStk) for (int i = numSlots - 1; i >= 0; --i) { - emitAttr slotAttr; - if (gcPtrs[i] == TYPE_GC_NONE) - { - slotAttr = EA_4BYTE; - } - else if (gcPtrs[i] == TYPE_GC_REF) - { - slotAttr = EA_GCREF; - } - else - { - assert(gcPtrs[i] == TYPE_GC_BYREF); - slotAttr = EA_BYREF; - } + var_types slotType = Compiler::getJitGCType(layout->GetGCPtr(i)); + emitAttr slotAttr = emitTypeSize(slotType); const unsigned offset = i * TARGET_POINTER_SIZE; if (srcAddrInReg) @@ -8369,11 +8357,11 @@ void CodeGen::genPutStructArgStk(GenTreePutArgStk* putArgStk) unsigned numGCSlotsCopied = 0; #endif // DEBUG - BYTE* gcPtrs = putArgStk->gtGcPtrs; - const unsigned numSlots = putArgStk->gtNumSlots; + StructTypeLayout* layout = putArgStk->gtLayout; + const unsigned numSlots = putArgStk->gtNumSlots; for (unsigned i = 0; i < numSlots;) { - if (gcPtrs[i] == TYPE_GC_NONE) + if (layout->GetGCPtr(i) == TYPE_GC_NONE) { // Let's see if we can use rep movsp (alias for movsd or movsq for 32 and 64 bits respectively) // instead of a sequence of movsp instructions to save cycles and code size. @@ -8382,7 +8370,7 @@ void CodeGen::genPutStructArgStk(GenTreePutArgStk* putArgStk) { adjacentNonGCSlotCount++; i++; - } while ((i < numSlots) && (gcPtrs[i] == TYPE_GC_NONE)); + } while ((i < numSlots) && (layout->GetGCPtr(i) == TYPE_GC_NONE)); // If we have a very small contiguous non-ref region, it's better just to // emit a sequence of movsp instructions @@ -8401,7 +8389,7 @@ void CodeGen::genPutStructArgStk(GenTreePutArgStk* putArgStk) } else { - assert((gcPtrs[i] == TYPE_GC_REF) || (gcPtrs[i] == TYPE_GC_BYREF)); + assert((layout->GetGCPtr(i) == TYPE_GC_REF) || (layout->GetGCPtr(i) == TYPE_GC_BYREF)); // We have a GC (byref or ref) pointer // TODO-Amd64-Unix: Here a better solution (for code size and CQ) would be to use movsp instruction, @@ -8409,7 +8397,7 @@ void CodeGen::genPutStructArgStk(GenTreePutArgStk* putArgStk) // only.) See emitGCVarLiveUpd function. If we could call it separately, we could do // instGen(INS_movsp); and emission of gc info. - var_types memType = (gcPtrs[i] == TYPE_GC_REF) ? TYP_REF : TYP_BYREF; + var_types memType = (layout->GetGCPtr(i) == TYPE_GC_REF) ? TYP_REF : TYP_BYREF; getEmitter()->emitIns_R_AR(ins_Load(memType), emitTypeSize(memType), REG_RCX, REG_RSI, 0); genStoreRegToStackArg(memType, REG_RCX, i * TARGET_POINTER_SIZE); #ifdef DEBUG diff --git a/src/jit/compiler.cpp b/src/jit/compiler.cpp index 8093a7defc01..a024d2436797 100644 --- a/src/jit/compiler.cpp +++ b/src/jit/compiler.cpp @@ -441,7 +441,7 @@ Histogram loopExitCountTable(loopExitCountBuckets); // the JIT will often pass the address of a single BYTE, instead of a BYTE[] // -var_types Compiler::getJitGCType(BYTE gcType) +/* static */ var_types Compiler::getJitGCType(BYTE gcType) { var_types result = TYP_UNKNOWN; CorInfoGCType corInfoType = (CorInfoGCType)gcType; @@ -465,61 +465,6 @@ var_types Compiler::getJitGCType(BYTE gcType) return result; } -#if FEATURE_MULTIREG_ARGS -//--------------------------------------------------------------------------- -// getStructGcPtrsFromOp: Given a GenTree node of TYP_STRUCT that represents -// a pass by value argument, return the gcPtr layout -// for the pointers sized fields -// Arguments: -// op - the operand of TYP_STRUCT that is passed by value -// gcPtrsOut - an array of BYTES that are written by this method -// they will contain the VM's CorInfoGCType values -// for each pointer sized field -// Return Value: -// Two [or more] values are written into the gcPtrs array -// -// Note that for ARM64 there will always be exactly two pointer sized fields - -void Compiler::getStructGcPtrsFromOp(GenTree* op, BYTE* gcPtrsOut) -{ - assert(op->TypeGet() == TYP_STRUCT); - -#ifdef _TARGET_ARM64_ - if (op->OperGet() == GT_OBJ) - { - CORINFO_CLASS_HANDLE objClass = op->gtObj.gtClass; - - int structSize = info.compCompHnd->getClassSize(objClass); - assert(structSize <= 2 * TARGET_POINTER_SIZE); - - BYTE gcPtrsTmp[2] = {TYPE_GC_NONE, TYPE_GC_NONE}; - - info.compCompHnd->getClassGClayout(objClass, &gcPtrsTmp[0]); - - gcPtrsOut[0] = gcPtrsTmp[0]; - gcPtrsOut[1] = gcPtrsTmp[1]; - } - else if (op->OperGet() == GT_LCL_VAR) - { - GenTreeLclVarCommon* varNode = op->AsLclVarCommon(); - unsigned varNum = varNode->gtLclNum; - assert(varNum < lvaCount); - LclVarDsc* varDsc = &lvaTable[varNum]; - - // At this point any TYP_STRUCT LclVar must be a 16-byte pass by value argument - assert(varDsc->lvSize() == 2 * TARGET_POINTER_SIZE); - - gcPtrsOut[0] = varDsc->lvGcLayout[0]; - gcPtrsOut[1] = varDsc->lvGcLayout[1]; - } - else -#endif - { - noway_assert(!"Unsupported Oper for getStructGcPtrsFromOp"); - } -} -#endif // FEATURE_MULTIREG_ARGS - #ifdef ARM_SOFTFP //--------------------------------------------------------------------------- // IsSingleFloat32Struct: @@ -2033,6 +1978,9 @@ void Compiler::compInit(ArenaAllocator* pAlloc, InlineInfo* inlineInfo) m_memorySsaMap[memoryKind] = nullptr; } + m_blkLayoutMap = nullptr; + m_objLayoutMap = nullptr; + #ifdef DEBUG if (!compIsForInlining()) { @@ -5064,28 +5012,11 @@ bool Compiler::compQuirkForPPP() // This fixes the PPP backward compat issue varDscExposedStruct->lvExactSize += 32; - // Update the GC info to indicate that the padding area does - // not contain any GC pointers. - // // The struct is now 64 bytes. - // // We're on x64 so this should be 8 pointer slots. assert((varDscExposedStruct->lvExactSize / TARGET_POINTER_SIZE) == 8); - BYTE* oldGCPtrs = varDscExposedStruct->lvGcLayout; - BYTE* newGCPtrs = getAllocator(CMK_LvaTable).allocate(8); - - for (int i = 0; i < 4; i++) - { - newGCPtrs[i] = oldGCPtrs[i]; - } - - for (int i = 4; i < 8; i++) - { - newGCPtrs[i] = TYPE_GC_NONE; - } - - varDscExposedStruct->lvGcLayout = newGCPtrs; + varDscExposedStruct->lvLayout = varDscExposedStruct->lvLayout->GetPPPQuirkLayout(getAllocator(CMK_Generic)); return true; } @@ -11062,3 +10993,55 @@ bool Compiler::killGCRefs(GenTree* tree) } return false; } + +StructTypeLayout* Compiler::typGetBlkLayout(unsigned blockSize) +{ + if (compIsForInlining()) + { + return impInlineInfo->InlinerCompiler->typGetBlkLayout(blockSize); + } + + if (m_blkLayoutMap == nullptr) + { + m_blkLayoutMap = new (this, CMK_Generic) + JitHashTable, StructTypeLayout>(getAllocator(CMK_Generic)); + } + + return m_blkLayoutMap->Emplace(blockSize, blockSize); +} + +StructTypeLayout* Compiler::typGetObjLayout(CORINFO_CLASS_HANDLE classHandle) +{ + if (compIsForInlining()) + { + return impInlineInfo->InlinerCompiler->typGetObjLayout(classHandle); + } + + if (m_objLayoutMap == nullptr) + { + m_objLayoutMap = new (this, CMK_Generic) + JitHashTable, StructTypeLayout>( + getAllocator(CMK_Generic)); + } + + StructTypeLayout* layout = m_objLayoutMap->LookupPointer(classHandle); + + if (layout == nullptr) + { + bool isValueClass = info.compCompHnd->isValueClass(classHandle); + unsigned size; + + if (isValueClass) + { + size = info.compCompHnd->getClassSize(classHandle); + } + else + { + size = info.compCompHnd->getHeapClassSize(classHandle); + } + + layout = m_objLayoutMap->Emplace(classHandle, classHandle, isValueClass, size); + } + + return layout; +} diff --git a/src/jit/compiler.h b/src/jit/compiler.h index 91e294a97ccd..2d8c572981d6 100644 --- a/src/jit/compiler.h +++ b/src/jit/compiler.h @@ -310,12 +310,10 @@ class LclVarDsc unsigned char lvIsRegArg : 1; // is this a register argument? unsigned char lvFramePointerBased : 1; // 0 = off of REG_SPBASE (e.g., ESP), 1 = off of REG_FPBASE (e.g., EBP) - unsigned char lvStructGcCount : 3; // if struct, how many GC pointer (stop counting at 7). The only use of values >1 - // is to help determine whether to use block init in the prolog. - unsigned char lvOnFrame : 1; // (part of) the variable lives on the frame - unsigned char lvRegister : 1; // assigned to live in a register? For RyuJIT backend, this is only set if the - // variable is in the same register for the entire function. - unsigned char lvTracked : 1; // is this a tracked variable? + unsigned char lvOnFrame : 1; // (part of) the variable lives on the frame + unsigned char lvRegister : 1; // assigned to live in a register? For RyuJIT backend, this is only set if the + // variable is in the same register for the entire function. + unsigned char lvTracked : 1; // is this a tracked variable? bool lvTrackedNonStruct() { return lvTracked && lvType != TYP_STRUCT; @@ -791,7 +789,7 @@ class LclVarDsc CORINFO_FIELD_HANDLE lvFieldHnd; // field handle for promoted struct fields - BYTE* lvGcLayout; // GC layout info for structs + StructTypeLayout* lvLayout; // layout info for structs #if ASSERTION_PROP BlockSet lvRefBlks; // Set of blocks that contain refs @@ -3222,7 +3220,6 @@ class Compiler } #endif // defined(FEATURE_SIMD) - BYTE* lvaGetGcLayout(unsigned varNum); bool lvaTypeIsGC(unsigned varNum); unsigned lvaGSSecurityCookie; // LclVar number bool lvaTempsHaveLargerOffsetThanVars(); @@ -3560,10 +3557,7 @@ class Compiler GenTree* impGetStructAddr(GenTree* structVal, CORINFO_CLASS_HANDLE structHnd, unsigned curLevel, bool willDeref); - var_types impNormStructType(CORINFO_CLASS_HANDLE structHnd, - BYTE* gcLayout = nullptr, - unsigned* numGCVars = nullptr, - var_types* simdBaseType = nullptr); + var_types impNormStructType(CORINFO_CLASS_HANDLE structHnd, var_types* simdBaseType = nullptr); GenTree* impNormStructVal(GenTree* structVal, CORINFO_CLASS_HANDLE structHnd, @@ -4525,7 +4519,7 @@ class Compiler } // Convert a BYTE which represents the VM's CorInfoGCtype to the JIT's var_types - var_types getJitGCType(BYTE gcType); + static var_types getJitGCType(BYTE gcType); enum structPassingKind { @@ -8800,12 +8794,6 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX #endif // FEATURE_MULTIREG_RET } -#if FEATURE_MULTIREG_ARGS - // Given a GenTree node of TYP_STRUCT that represents a pass by value argument - // return the gcPtr layout for the pointers sized fields - void getStructGcPtrsFromOp(GenTree* op, BYTE* gcPtrsOut); -#endif // FEATURE_MULTIREG_ARGS - // Returns true if the method being compiled returns a value bool compMethodHasRetVal() { @@ -8819,6 +8807,12 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX #endif // DEBUG + JitHashTable, StructTypeLayout>* m_blkLayoutMap; + JitHashTable, StructTypeLayout>* m_objLayoutMap; + + StructTypeLayout* typGetBlkLayout(unsigned blockSize); + StructTypeLayout* typGetObjLayout(CORINFO_CLASS_HANDLE classHandle); + //-------------------------- Global Compiler Data ------------------------------------ #ifdef DEBUG diff --git a/src/jit/compiler.hpp b/src/jit/compiler.hpp index 3fceff729a7d..b837e14d1105 100644 --- a/src/jit/compiler.hpp +++ b/src/jit/compiler.hpp @@ -1953,10 +1953,12 @@ inline bool Compiler::lvaTypeIsGC(unsigned varNum) { if (lvaTable[varNum].TypeGet() == TYP_STRUCT) { - assert(lvaTable[varNum].lvGcLayout != nullptr); // bits are intialized - return (lvaTable[varNum].lvStructGcCount != 0); + return lvaTable[varNum].lvLayout->HasGCPtr(); + } + else + { + return varTypeIsGC(lvaTable[varNum].TypeGet()); } - return (varTypeIsGC(lvaTable[varNum].TypeGet())); } /***************************************************************************** @@ -4217,7 +4219,8 @@ inline void Compiler::CLR_API_Leave(API_ICorJitInfo_Names ename) bool Compiler::fgStructTempNeedsExplicitZeroInit(LclVarDsc* varDsc, BasicBlock* block) { - bool containsGCPtr = (varDsc->lvStructGcCount > 0); + bool containsGCPtr = + varTypeIsGC(varDsc->TypeGet()) || ((varDsc->TypeGet() == TYP_STRUCT) && varDsc->lvLayout->HasGCPtr()); return (!info.compInitMem || ((block->bbFlags & BBF_BACKWARD_JUMP) != 0) || (!containsGCPtr && varDsc->lvIsTemp)); } diff --git a/src/jit/gcencode.cpp b/src/jit/gcencode.cpp index af37304d48bd..c3885454f96e 100644 --- a/src/jit/gcencode.cpp +++ b/src/jit/gcencode.cpp @@ -2321,17 +2321,24 @@ size_t GCInfo::gcMakeRegPtrTable(BYTE* dest, int mask, const InfoHdr& header, un // A struct will have gcSlots only if it is at least TARGET_POINTER_SIZE. if (varDsc->lvType == TYP_STRUCT && varDsc->lvOnFrame && (varDsc->lvExactSize >= TARGET_POINTER_SIZE)) { - unsigned slots = compiler->lvaLclSize(varNum) / TARGET_POINTER_SIZE; - BYTE* gcPtrs = compiler->lvaGetGcLayout(varNum); + StructTypeLayout* layout = varDsc->lvLayout; + const unsigned slots = varDsc->lvLayout->GetSlotCount(); // walk each member of the array for (unsigned i = 0; i < slots; i++) { - if (gcPtrs[i] == TYPE_GC_NONE) // skip non-gc slots + CorInfoGCType gcType = layout->GetGCPtr(i); + + if (gcType == TYPE_GC_NONE) + { + // skip non-gc slots continue; + } if (pass == 0) + { count++; + } else { assert(pass == 1); @@ -2342,16 +2349,22 @@ size_t GCInfo::gcMakeRegPtrTable(BYTE* dest, int mask, const InfoHdr& header, un // arguments are addressed relative to EBP. if (compiler->genDoubleAlign() && varDsc->lvIsParam && !varDsc->lvIsRegArg) + { offset += compiler->codeGen->genTotalFrameSize(); + } #endif - if (gcPtrs[i] == TYPE_GC_BYREF) + if (gcType == TYPE_GC_BYREF) + { offset |= byref_OFFSET_FLAG; // indicate it is a byref GC pointer + } int encodedoffset = lastoffset - offset; lastoffset = offset; if (mask == 0) + { totalSize += encodeSigned(NULL, encodedoffset); + } else { unsigned sz = encodeSigned(dest, encodedoffset); @@ -4284,14 +4297,17 @@ void GCInfo::gcMakeRegPtrTable( // Note that the enregisterable struct types cannot have GC pointers in them. if ((varDsc->lvType == TYP_STRUCT) && varDsc->lvOnFrame && (varDsc->lvExactSize >= TARGET_POINTER_SIZE)) { - unsigned slots = compiler->lvaLclSize(varNum) / TARGET_POINTER_SIZE; - BYTE* gcPtrs = compiler->lvaGetGcLayout(varNum); + StructTypeLayout* layout = varDsc->lvLayout; + const unsigned slots = layout->GetSlotCount(); // walk each member of the array for (unsigned i = 0; i < slots; i++) { - if (gcPtrs[i] == TYPE_GC_NONE) - { // skip non-gc slots + CorInfoGCType gcType = layout->GetGCPtr(i); + + if (gcType == TYPE_GC_NONE) + { + // skip non-gc slots continue; } @@ -4304,7 +4320,7 @@ void GCInfo::gcMakeRegPtrTable( offset += compiler->codeGen->genTotalFrameSize(); #endif GcSlotFlags flags = GC_SLOT_UNTRACKED; - if (gcPtrs[i] == TYPE_GC_BYREF) + if (gcType == TYPE_GC_BYREF) { flags = (GcSlotFlags)(flags | GC_SLOT_INTERIOR); } diff --git a/src/jit/gcinfo.cpp b/src/jit/gcinfo.cpp index ed4c468de9b0..858e92899566 100644 --- a/src/jit/gcinfo.cpp +++ b/src/jit/gcinfo.cpp @@ -471,19 +471,9 @@ void GCInfo::gcCountForHeader(UNALIGNED unsigned int* untrackedCount, UNALIGNED count++; } - else if (varDsc->lvType == TYP_STRUCT && varDsc->lvOnFrame && (varDsc->lvExactSize >= TARGET_POINTER_SIZE)) + else if ((varDsc->lvType == TYP_STRUCT) && varDsc->lvOnFrame && (varDsc->lvExactSize >= TARGET_POINTER_SIZE)) { - unsigned slots = compiler->lvaLclSize(varNum) / TARGET_POINTER_SIZE; - BYTE* gcPtrs = compiler->lvaGetGcLayout(varNum); - - // walk each member of the array - for (unsigned i = 0; i < slots; i++) - { - if (gcPtrs[i] != TYPE_GC_NONE) - { // count only gc slots - count++; - } - } + count += varDsc->lvLayout->GetGCPtrCount(); } } diff --git a/src/jit/gentree.cpp b/src/jit/gentree.cpp index 5d00ebb06e59..f8a4ded3f940 100644 --- a/src/jit/gentree.cpp +++ b/src/jit/gentree.cpp @@ -264,13 +264,11 @@ void GenTree::InitNodeSize() GenTree::s_gtNodeSizes[GT_ARR_INDEX] = TREE_NODE_SZ_LARGE; GenTree::s_gtNodeSizes[GT_ARR_OFFSET] = TREE_NODE_SZ_LARGE; GenTree::s_gtNodeSizes[GT_RET_EXPR] = TREE_NODE_SZ_LARGE; - GenTree::s_gtNodeSizes[GT_OBJ] = TREE_NODE_SZ_LARGE; GenTree::s_gtNodeSizes[GT_FIELD] = TREE_NODE_SZ_LARGE; GenTree::s_gtNodeSizes[GT_STMT] = TREE_NODE_SZ_LARGE; GenTree::s_gtNodeSizes[GT_CMPXCHG] = TREE_NODE_SZ_LARGE; GenTree::s_gtNodeSizes[GT_QMARK] = TREE_NODE_SZ_LARGE; GenTree::s_gtNodeSizes[GT_LEA] = TREE_NODE_SZ_LARGE; - GenTree::s_gtNodeSizes[GT_STORE_OBJ] = TREE_NODE_SZ_LARGE; GenTree::s_gtNodeSizes[GT_DYN_BLK] = TREE_NODE_SZ_LARGE; GenTree::s_gtNodeSizes[GT_STORE_DYN_BLK] = TREE_NODE_SZ_LARGE; GenTree::s_gtNodeSizes[GT_INTRINSIC] = TREE_NODE_SZ_LARGE; @@ -332,8 +330,9 @@ void GenTree::InitNodeSize() static_assert_no_msg(sizeof(GenTreeIndir) <= TREE_NODE_SZ_SMALL); static_assert_no_msg(sizeof(GenTreeStoreInd) <= TREE_NODE_SZ_SMALL); static_assert_no_msg(sizeof(GenTreeAddrMode) <= TREE_NODE_SZ_SMALL); - static_assert_no_msg(sizeof(GenTreeObj) <= TREE_NODE_SZ_LARGE); // *** large node + static_assert_no_msg(sizeof(GenTreeObj) <= TREE_NODE_SZ_SMALL); static_assert_no_msg(sizeof(GenTreeBlk) <= TREE_NODE_SZ_SMALL); + static_assert_no_msg(sizeof(GenTreeDynBlk) <= TREE_NODE_SZ_LARGE); // *** large node static_assert_no_msg(sizeof(GenTreeRetExpr) <= TREE_NODE_SZ_LARGE); // *** large node static_assert_no_msg(sizeof(GenTreeStmt) <= TREE_NODE_SZ_LARGE); // *** large node static_assert_no_msg(sizeof(GenTreeClsVar) <= TREE_NODE_SZ_SMALL); @@ -1288,7 +1287,7 @@ bool GenTree::Compare(GenTree* op1, GenTree* op2, bool swapOK) } break; case GT_OBJ: - if (op1->AsObj()->gtClass != op2->AsObj()->gtClass) + if (op1->AsObj()->gtLayout->GetClass() != op2->AsObj()->gtLayout->GetClass()) { return false; } @@ -1980,8 +1979,8 @@ unsigned Compiler::gtHashValue(GenTree* tree) break; case GT_OBJ: - hash = - genTreeHashAdd(hash, static_cast(reinterpret_cast(tree->gtObj.gtClass))); + hash = genTreeHashAdd(hash, static_cast( + reinterpret_cast(tree->AsObj()->gtLayout->GetClass()))); break; // For the ones below no extra argument matters for comparison. case GT_BOX: @@ -2018,12 +2017,12 @@ unsigned Compiler::gtHashValue(GenTree* tree) case GT_BLK: case GT_STORE_BLK: - hash += tree->gtBlk.gtBlkSize; + hash += tree->gtBlk.Size(); break; case GT_OBJ: case GT_STORE_OBJ: - hash ^= PtrToUlong(tree->AsObj()->gtClass); + hash ^= PtrToUlong(tree->AsObj()->gtLayout->GetClass()); break; case GT_DYN_BLK: @@ -6213,7 +6212,6 @@ GenTree* Compiler::gtNewObjNode(CORINFO_CLASS_HANDLE structHnd, GenTree* addr) { var_types nodeType = impNormStructType(structHnd); assert(varTypeIsStruct(nodeType)); - unsigned size = info.compCompHnd->getClassSize(structHnd); // It would be convenient to set the GC info at this time, but we don't actually require // it unless this is going to be a destination. @@ -6228,7 +6226,7 @@ GenTree* Compiler::gtNewObjNode(CORINFO_CLASS_HANDLE structHnd, GenTree* addr) return gtNewOperNode(GT_IND, nodeType, addr); } } - GenTreeBlk* newBlkOrObjNode = new (this, GT_OBJ) GenTreeObj(nodeType, addr, structHnd, size); + GenTreeBlk* newBlkOrObjNode = new (this, GT_OBJ) GenTreeObj(nodeType, addr, typGetObjLayout(structHnd)); // An Obj is not a global reference, if it is known to be a local struct. if ((addr->gtFlags & GTF_GLOB_REF) == 0) @@ -6254,30 +6252,16 @@ GenTree* Compiler::gtNewObjNode(CORINFO_CLASS_HANDLE structHnd, GenTree* addr) void Compiler::gtSetObjGcInfo(GenTreeObj* objNode) { - CORINFO_CLASS_HANDLE structHnd = objNode->gtClass; - var_types nodeType = objNode->TypeGet(); - unsigned size = objNode->gtBlkSize; - unsigned slots = 0; - unsigned gcPtrCount = 0; - BYTE* gcPtrs = nullptr; + assert(objNode->OperIs(GT_OBJ, GT_STORE_OBJ)); + assert(varTypeIsStruct(objNode->TypeGet())); + assert(objNode->TypeGet() == impNormStructType(objNode->gtLayout->GetClass())); - assert(varTypeIsStruct(nodeType)); - assert(size == info.compCompHnd->getClassSize(structHnd)); - assert(nodeType == impNormStructType(structHnd)); + objNode->gtLayout->EnsureGCInfoInitialized(info.compCompHnd, getAllocator(CMK_GC)); - if (nodeType == TYP_STRUCT) + if (!objNode->gtLayout->HasGCPtr()) { - if (size >= TARGET_POINTER_SIZE) - { - // Get the GC fields info - var_types simdBaseType; // Dummy argument - slots = roundUp(size, TARGET_POINTER_SIZE) / TARGET_POINTER_SIZE; - gcPtrs = new (this, CMK_ASTNode) BYTE[slots]; - nodeType = impNormStructType(structHnd, gcPtrs, &gcPtrCount, &simdBaseType); - } + objNode->SetOper(objNode->OperIs(GT_OBJ) ? GT_BLK : GT_STORE_BLK); } - objNode->SetGCInfo(gcPtrs, gcPtrCount, slots); - assert(objNode->gtType == nodeType); } //------------------------------------------------------------------------ @@ -6347,7 +6331,7 @@ GenTree* Compiler::gtNewBlockVal(GenTree* addr, unsigned size) } } } - return new (this, GT_BLK) GenTreeBlk(GT_BLK, blkType, addr, size); + return new (this, GT_BLK) GenTreeBlk(GT_BLK, blkType, addr, typGetBlkLayout(size)); } // Creates a new assignment node for a CpObj. @@ -6367,7 +6351,7 @@ GenTree* Compiler::gtNewCpObjNode(GenTree* dstAddr, GenTree* srcAddr, CORINFO_CL if (lhs->OperIsBlk()) { - size = lhs->AsBlk()->gtBlkSize; + size = lhs->AsBlk()->Size(); if (lhs->OperGet() == GT_OBJ) { gtSetObjGcInfo(lhs->AsObj()); @@ -6462,33 +6446,6 @@ void Compiler::gtBlockOpInit(GenTree* result, GenTree* dst, GenTree* srcOrFillVa assert(dst->TypeGet() != TYP_STRUCT); return; } -#ifdef DEBUG - // If the copy involves GC pointers, the caller must have already set - // the node additional members (gtGcPtrs, gtGcPtrCount, gtSlots) on the dst. - if ((dst->gtOper == GT_OBJ) && dst->AsBlk()->HasGCPtr()) - { - GenTreeObj* objNode = dst->AsObj(); - assert(objNode->gtGcPtrs != nullptr); - assert(!IsUninitialized(objNode->gtGcPtrs)); - assert(!IsUninitialized(objNode->gtGcPtrCount)); - assert(!IsUninitialized(objNode->gtSlots) && objNode->gtSlots > 0); - - for (unsigned i = 0; i < objNode->gtGcPtrCount; ++i) - { - CorInfoGCType t = (CorInfoGCType)objNode->gtGcPtrs[i]; - switch (t) - { - case TYPE_GC_NONE: - case TYPE_GC_REF: - case TYPE_GC_BYREF: - case TYPE_GC_OTHER: - break; - default: - unreached(); - } - } - } -#endif // DEBUG /* In the case of CpBlk, we want to avoid generating * nodes where the source and destination are the same @@ -7175,14 +7132,13 @@ GenTree* Compiler::gtCloneExpr( break; case GT_OBJ: - copy = new (this, GT_OBJ) - GenTreeObj(tree->TypeGet(), tree->gtOp.gtOp1, tree->AsObj()->gtClass, tree->gtBlk.gtBlkSize); - copy->AsObj()->CopyGCInfo(tree->AsObj()); + copy = new (this, GT_OBJ) GenTreeObj(tree->TypeGet(), tree->gtOp.gtOp1, tree->AsObj()->gtLayout); copy->gtBlk.gtBlkOpGcUnsafe = tree->gtBlk.gtBlkOpGcUnsafe; break; case GT_BLK: - copy = new (this, GT_BLK) GenTreeBlk(GT_BLK, tree->TypeGet(), tree->gtOp.gtOp1, tree->gtBlk.gtBlkSize); + copy = + new (this, GT_BLK) GenTreeBlk(GT_BLK, tree->TypeGet(), tree->gtOp.gtOp1, tree->AsBlk()->gtLayout); copy->gtBlk.gtBlkOpGcUnsafe = tree->gtBlk.gtBlkOpGcUnsafe; break; @@ -9223,7 +9179,7 @@ void Compiler::gtDispNodeName(GenTree* tree) } else if (tree->OperIsBlk() && !tree->OperIsDynBlk()) { - sprintf_s(bufp, sizeof(buf), " %s(%d)", name, tree->AsBlk()->gtBlkSize); + sprintf_s(bufp, sizeof(buf), " %s(%d)", name, tree->AsBlk()->Size()); } else { @@ -14474,12 +14430,8 @@ GenTree* Compiler::gtNewTempAssign( if (dstTyp == TYP_UNDEF) { varDsc->lvType = dstTyp = genActualType(valTyp); - if (varTypeIsGC(dstTyp)) - { - varDsc->lvStructGcCount = 1; - } #if FEATURE_SIMD - else if (varTypeIsSIMD(dstTyp)) + if (varTypeIsSIMD(dstTyp)) { varDsc->lvSIMDType = 1; } @@ -15476,7 +15428,7 @@ bool GenTree::DefinesLocal(Compiler* comp, GenTreeLclVarCommon** pLclVarTree, bo if (blkNode != nullptr) { GenTree* destAddr = blkNode->Addr(); - unsigned width = blkNode->gtBlkSize; + unsigned width = blkNode->Size(); // Do we care about whether this assigns the entire variable? if (pIsEntire != nullptr && width == 0) { @@ -16504,7 +16456,7 @@ CORINFO_CLASS_HANDLE Compiler::gtGetStructHandleIfPresent(GenTree* tree) structHnd = impGetRefAnyClass(); break; case GT_OBJ: - structHnd = tree->gtObj.gtClass; + structHnd = tree->gtObj.gtLayout->GetClass(); break; case GT_CALL: structHnd = tree->gtCall.gtRetClsHnd; @@ -18111,7 +18063,7 @@ void ReturnTypeDesc::InitializeStructReturnType(Compiler* comp, CORINFO_CLASS_HA comp->info.compCompHnd->getClassGClayout(retClsHnd, &gcPtrs[0]); for (unsigned i = 0; i < 2; ++i) { - m_regType[i] = comp->getJitGCType(gcPtrs[i]); + m_regType[i] = Compiler::getJitGCType(gcPtrs[i]); } #else // _TARGET_XXX_ diff --git a/src/jit/gentree.h b/src/jit/gentree.h index 9846ceab0909..308ac99bf560 100644 --- a/src/jit/gentree.h +++ b/src/jit/gentree.h @@ -65,6 +65,190 @@ enum SpecialCodeKind SCK_COUNT }; +class StructTypeLayout +{ + // the class of the object, may be NO_CLASS_HANDLE + const CORINFO_CLASS_HANDLE m_classHandle; + + // the size (in bytes) of the struct + const unsigned m_size; + + unsigned m_isValueClass : 1; + unsigned m_gcPtrsInitialized : 1; + unsigned m_gcPtrCount : 30; + + // If non-null, this array represents the gc-layout of the class. + // This may be simply copied when cloning this node, because it is not changed once computed. + union { + BYTE* m_gcPtrs; + BYTE m_gcPtrsArray[sizeof(void*)]; + }; + +#ifdef _TARGET_AMD64_ + StructTypeLayout* m_pppQuirkLayout; +#endif + +public: + StructTypeLayout(unsigned size) + : m_classHandle(NO_CLASS_HANDLE) + , m_size(size) + , m_isValueClass(false) + , m_gcPtrsInitialized(false) + , m_gcPtrCount(0) + , m_gcPtrs(nullptr) +#ifdef _TARGET_AMD64_ + , m_pppQuirkLayout(nullptr) +#endif + { + } + + StructTypeLayout(CORINFO_CLASS_HANDLE classHandle, bool isValueClass, unsigned size) + : m_classHandle(classHandle) + , m_size(size) + , m_isValueClass(isValueClass) + , m_gcPtrsInitialized(false) + , m_gcPtrCount(0) + , m_gcPtrs(nullptr) +#ifdef _TARGET_AMD64_ + , m_pppQuirkLayout(nullptr) +#endif + { + } + + CORINFO_CLASS_HANDLE GetClass() const + { + return m_classHandle; + } + + bool IsValueClass() const + { + assert(m_classHandle != NO_CLASS_HANDLE); + + return m_isValueClass; + } + + unsigned GetSize() const + { + return m_size; + } + + unsigned GetSlotCount() const + { + return (m_size < TARGET_POINTER_SIZE) ? 0 : (roundUp(m_size, TARGET_POINTER_SIZE) / TARGET_POINTER_SIZE); + } + + bool IsGCInfoInitialized() const + { + return m_gcPtrsInitialized; + } + + void EnsureGCInfoInitialized(ICorJitInfo* jitInfo, CompAllocator alloc) + { + assert(m_classHandle != NO_CLASS_HANDLE); + + if (m_gcPtrsInitialized) + { + return; + } + + if ((m_size < TARGET_POINTER_SIZE) || (m_classHandle == NO_CLASS_HANDLE)) + { + m_gcPtrCount = 0; + m_gcPtrsInitialized = true; + } + else + { + BYTE* gcPtrs; + + if (GetSlotCount() > sizeof(m_gcPtrsArray)) + { + gcPtrs = m_gcPtrs = new (alloc) BYTE[GetSlotCount()]; + } + else + { + gcPtrs = m_gcPtrsArray; + } + + unsigned gcPtrCount = jitInfo->getClassGClayout(m_classHandle, gcPtrs); + + assert(gcPtrCount < (1 << 30)); + + assert((gcPtrCount == 0) || ((jitInfo->getClassAttribs(m_classHandle) & + (CORINFO_FLG_CONTAINS_GC_PTR | CORINFO_FLG_CONTAINS_STACK_PTR)) != 0)); + + // We assume that we cannot have a struct with GC pointers that is not a multiple + // of the register size. + // The EE currently does not allow this, but it could change. + // Let's assert it just to be safe. + noway_assert((gcPtrCount == 0) || (roundUp(m_size, REGSIZE_BYTES) == m_size)); + + m_gcPtrCount = gcPtrCount; + m_gcPtrsInitialized = true; + } + } + + unsigned GetGCPtrCount() const + { + assert(m_gcPtrsInitialized); + + return m_gcPtrCount; + } + + bool HasGCPtr() const + { + assert(m_gcPtrsInitialized); + + return m_gcPtrCount != 0; + } + +private: + const BYTE* GetGCPtrs() const + { + assert(m_gcPtrsInitialized); + assert(m_classHandle != NO_CLASS_HANDLE); + + return (GetSlotCount() > sizeof(m_gcPtrsArray)) ? m_gcPtrs : m_gcPtrsArray; + } + +public: + CorInfoGCType GetGCPtr(unsigned slot) const + { + assert(slot < GetSlotCount()); + + return static_cast(GetGCPtrs()[slot]); + } + +#ifdef _TARGET_AMD64_ + StructTypeLayout* GetPPPQuirkLayout(CompAllocator alloc) + { + assert(m_gcPtrsInitialized); + assert(m_classHandle != NO_CLASS_HANDLE); + assert(m_isValueClass); + assert(m_size == 32); + + if (m_pppQuirkLayout == nullptr) + { + m_pppQuirkLayout = new (alloc) StructTypeLayout(m_classHandle, m_isValueClass, 64); + m_pppQuirkLayout->m_gcPtrCount = m_gcPtrCount; + + static_assert_no_msg(_countof(m_gcPtrsArray) == 8); + + for (int i = 0; i < 4; i++) + { + m_pppQuirkLayout->m_gcPtrsArray[i] = m_gcPtrsArray[i]; + } + + for (int i = 4; i < 8; i++) + { + m_pppQuirkLayout->m_gcPtrsArray[i] = TYPE_GC_NONE; + } + } + + return m_pppQuirkLayout; + } +#endif // _TARGET_AMD64_ +}; + /*****************************************************************************/ enum genTreeOps : BYTE @@ -4607,6 +4791,8 @@ struct GenTreeIndir : public GenTreeOp struct GenTreeBlk : public GenTreeIndir { public: + StructTypeLayout* gtLayout; + // The data to be stored (null for GT_BLK) GenTree*& Data() { @@ -4620,13 +4806,15 @@ struct GenTreeBlk : public GenTreeIndir // The size of the buffer to be copied. unsigned Size() const { - return gtBlkSize; + assert((gtLayout != nullptr) || OperIs(GT_DYN_BLK, GT_STORE_DYN_BLK)); + return (gtLayout != nullptr) ? gtLayout->GetSize() : 0; } - unsigned gtBlkSize; - // Return true iff the object being copied contains one or more GC pointers. - bool HasGCPtr(); + bool HasGCPtr() + { + return OperIs(GT_OBJ, GT_STORE_OBJ) && gtLayout->HasGCPtr(); + } // True if this BlkOpNode is a volatile memory operation. bool IsVolatile() const @@ -4652,20 +4840,22 @@ struct GenTreeBlk : public GenTreeIndir bool gtBlkOpGcUnsafe; - GenTreeBlk(genTreeOps oper, var_types type, GenTree* addr, unsigned size) + GenTreeBlk(genTreeOps oper, var_types type, GenTree* addr, StructTypeLayout* layout) : GenTreeIndir(oper, type, addr, nullptr) - , gtBlkSize(size) + , gtLayout(layout) , gtBlkOpKind(BlkOpKindInvalid) , gtBlkOpGcUnsafe(false) { assert(OperIsBlk(oper)); + assert((layout != nullptr) || OperIs(GT_DYN_BLK, GT_STORE_DYN_BLK)); gtFlags |= (addr->gtFlags & GTF_ALL_EFFECT); } - GenTreeBlk(genTreeOps oper, var_types type, GenTree* addr, GenTree* data, unsigned size) - : GenTreeIndir(oper, type, addr, data), gtBlkSize(size), gtBlkOpKind(BlkOpKindInvalid), gtBlkOpGcUnsafe(false) + GenTreeBlk(genTreeOps oper, var_types type, GenTree* addr, GenTree* data, StructTypeLayout* layout) + : GenTreeIndir(oper, type, addr, data), gtLayout(layout), gtBlkOpKind(BlkOpKindInvalid), gtBlkOpGcUnsafe(false) { assert(OperIsBlk(oper)); + assert((layout != nullptr) || OperIs(GT_DYN_BLK, GT_STORE_DYN_BLK)); gtFlags |= (addr->gtFlags & GTF_ALL_EFFECT); gtFlags |= (data->gtFlags & GTF_ALL_EFFECT); } @@ -4685,84 +4875,19 @@ struct GenTreeBlk : public GenTreeIndir struct GenTreeObj : public GenTreeBlk { - CORINFO_CLASS_HANDLE gtClass; // the class of the object - - // If non-null, this array represents the gc-layout of the class. - // This may be simply copied when cloning this node, because it is not changed once computed. - BYTE* gtGcPtrs; - - // If non-zero, this is the number of slots in the class layout that - // contain gc-pointers. - __declspec(property(get = GetGcPtrCount)) unsigned gtGcPtrCount; - unsigned GetGcPtrCount() const - { - assert(_gtGcPtrCount != UINT32_MAX); - return _gtGcPtrCount; - } - unsigned _gtGcPtrCount; - - // If non-zero, the number of pointer-sized slots that constitutes the class token. - unsigned gtSlots; - - bool IsGCInfoInitialized() - { - return (_gtGcPtrCount != UINT32_MAX); - } - - void SetGCInfo(BYTE* gcPtrs, unsigned gcPtrCount, unsigned slots) - { - gtGcPtrs = gcPtrs; - _gtGcPtrCount = gcPtrCount; - gtSlots = slots; - if (gtGcPtrCount != 0) - { - // We assume that we cannot have a struct with GC pointers that is not a multiple - // of the register size. - // The EE currently does not allow this, but it could change. - // Let's assert it just to be safe. - noway_assert(roundUp(gtBlkSize, REGSIZE_BYTES) == gtBlkSize); - } - else - { - genTreeOps newOper = GT_BLK; - if (gtOper == GT_STORE_OBJ) - { - newOper = GT_STORE_BLK; - } - else - { - assert(gtOper == GT_OBJ); - } - SetOper(newOper); - } - } - - void CopyGCInfo(GenTreeObj* srcObj) - { - if (srcObj->IsGCInfoInitialized()) - { - gtGcPtrs = srcObj->gtGcPtrs; - _gtGcPtrCount = srcObj->gtGcPtrCount; - gtSlots = srcObj->gtSlots; - } - } - - GenTreeObj(var_types type, GenTree* addr, CORINFO_CLASS_HANDLE cls, unsigned size) - : GenTreeBlk(GT_OBJ, type, addr, size), gtClass(cls) + GenTreeObj(var_types type, GenTree* addr, StructTypeLayout* layout) : GenTreeBlk(GT_OBJ, type, addr, layout) { // By default, an OBJ is assumed to be a global reference. gtFlags |= GTF_GLOB_REF; - noway_assert(cls != NO_CLASS_HANDLE); - _gtGcPtrCount = UINT32_MAX; + noway_assert(layout->GetClass() != NO_CLASS_HANDLE); } - GenTreeObj(var_types type, GenTree* addr, GenTree* data, CORINFO_CLASS_HANDLE cls, unsigned size) - : GenTreeBlk(GT_STORE_OBJ, type, addr, data, size), gtClass(cls) + GenTreeObj(var_types type, GenTree* addr, GenTree* data, StructTypeLayout* layout) + : GenTreeBlk(GT_STORE_OBJ, type, addr, data, layout) { // By default, an OBJ is assumed to be a global reference. gtFlags |= GTF_GLOB_REF; - noway_assert(cls != NO_CLASS_HANDLE); - _gtGcPtrCount = UINT32_MAX; + noway_assert(layout->GetClass() != NO_CLASS_HANDLE); } #if DEBUGGABLE_GENTREE @@ -4784,7 +4909,7 @@ struct GenTreeDynBlk : public GenTreeBlk bool gtEvalSizeFirst; GenTreeDynBlk(GenTree* addr, GenTree* dynamicSize) - : GenTreeBlk(GT_DYN_BLK, TYP_STRUCT, addr, 0), gtDynamicSize(dynamicSize), gtEvalSizeFirst(false) + : GenTreeBlk(GT_DYN_BLK, TYP_STRUCT, addr, nullptr), gtDynamicSize(dynamicSize), gtEvalSizeFirst(false) { // Conservatively the 'addr' could be null or point into the global heap. gtFlags |= GTF_EXCEPT | GTF_GLOB_REF; @@ -5082,7 +5207,7 @@ struct GenTreePutArgStk : public GenTreeUnOp , gtPutArgStkKind(Kind::Invalid) , gtNumSlots(numSlots) , gtNumberReferenceSlots(0) - , gtGcPtrs(nullptr) + , gtLayout(nullptr) #endif // FEATURE_PUT_STRUCT_ARG_STK #if defined(DEBUG) || defined(UNIX_X86_ABI) , gtCall(callNode) @@ -5160,10 +5285,12 @@ struct GenTreePutArgStk : public GenTreeUnOp // Otherwise the pointer reference slots are copied atomically in a way that gcinfo is emitted. // Any non pointer references between the pointer reference slots are copied in block fashion. // - void setGcPointers(unsigned numPointers, BYTE* pointers) + void setLayout(StructTypeLayout* layout) { - gtNumberReferenceSlots = numPointers; - gtGcPtrs = pointers; + assert(layout->IsGCInfoInitialized()); + + gtNumberReferenceSlots = layout->GetGCPtrCount(); + gtLayout = layout; } // Instruction selection: during codegen time, what code sequence we will be using @@ -5181,9 +5308,9 @@ struct GenTreePutArgStk : public GenTreeUnOp return (gtPutArgStkKind == Kind::Push) || (gtPutArgStkKind == Kind::PushAllSlots); } - unsigned gtNumSlots; // Number of slots for the argument to be passed on stack - unsigned gtNumberReferenceSlots; // Number of reference slots. - BYTE* gtGcPtrs; // gcPointers + unsigned gtNumSlots; // Number of slots for the argument to be passed on stack + unsigned gtNumberReferenceSlots; // Number of reference slots. + StructTypeLayout* gtLayout; #else // !FEATURE_PUT_STRUCT_ARG_STK unsigned getArgSize(); @@ -6284,27 +6411,6 @@ inline var_types& GenTree::CastToType() return this->gtCast.gtCastType; } -//----------------------------------------------------------------------------------- -// HasGCPtr: determine whether this block op involves GC pointers -// -// Arguments: -// None -// -// Return Value: -// Returns true iff the object being copied contains one or more GC pointers. -// -// Notes: -// Of the block nodes, only GT_OBJ and ST_STORE_OBJ are allowed to have GC pointers. -// -inline bool GenTreeBlk::HasGCPtr() -{ - if ((gtOper == GT_OBJ) || (gtOper == GT_STORE_OBJ)) - { - return (AsObj()->gtGcPtrCount != 0); - } - return false; -} - inline bool GenTree::isUsedFromSpillTemp() const { // If spilled and no reg at use, then it is used from the spill temp location rather than being reloaded. diff --git a/src/jit/gschecks.cpp b/src/jit/gschecks.cpp index 8fe14b9d9d8b..236286d79337 100644 --- a/src/jit/gschecks.cpp +++ b/src/jit/gschecks.cpp @@ -427,7 +427,7 @@ void Compiler::gsParamsToShadows() lvaTable[shadowVar].lvLiveAcrossUCall = varDsc->lvLiveAcrossUCall; #endif lvaTable[shadowVar].lvVerTypeInfo = varDsc->lvVerTypeInfo; - lvaTable[shadowVar].lvGcLayout = varDsc->lvGcLayout; + lvaTable[shadowVar].lvLayout = varDsc->lvLayout; lvaTable[shadowVar].lvIsUnsafeBuffer = varDsc->lvIsUnsafeBuffer; lvaTable[shadowVar].lvIsPtr = varDsc->lvIsPtr; diff --git a/src/jit/importer.cpp b/src/jit/importer.cpp index 512521d0e79f..b14ab31f817b 100644 --- a/src/jit/importer.cpp +++ b/src/jit/importer.cpp @@ -609,8 +609,7 @@ inline void Compiler::impAppendStmt(GenTree* stmt, unsigned chkLevel) } } else if ((lhs->OperIsBlk() && !lhs->AsBlk()->HasGCPtr()) || - ((lhs->OperGet() == GT_LCL_VAR) && - (lvaTable[lhs->AsLclVarCommon()->gtLclNum].lvStructGcCount == 0))) + (lhs->OperIs(GT_LCL_VAR) && !lvaTypeIsGC(lhs->AsLclVar()->GetLclNum()))) { spillGlobEffects = true; } @@ -1293,7 +1292,7 @@ GenTree* Compiler::impAssignStructPtr(GenTree* destAddr, asgType = impNormStructType(structHnd); if (src->gtOper == GT_OBJ) { - assert(src->gtObj.gtClass == structHnd); + assert(src->gtObj.gtLayout->GetClass() == structHnd); } } else if (src->gtOper == GT_INDEX) @@ -1374,7 +1373,7 @@ GenTree* Compiler::impAssignStructPtr(GenTree* destAddr, } else if (varTypeIsStruct(asgType)) { - dest = new (this, GT_BLK) GenTreeBlk(GT_BLK, asgType, destAddr, genTypeSize(asgType)); + dest = new (this, GT_BLK) GenTreeBlk(GT_BLK, asgType, destAddr, typGetBlkLayout(genTypeSize(asgType))); } else { @@ -1422,7 +1421,7 @@ GenTree* Compiler::impGetStructAddr(GenTree* structVal, if (oper == GT_OBJ && willDeref) { - assert(structVal->gtObj.gtClass == structHnd); + assert(structVal->gtObj.gtLayout->GetClass() == structHnd); return (structVal->gtObj.Addr()); } else if (oper == GT_CALL || oper == GT_RET_EXPR || oper == GT_OBJ || oper == GT_MKREFANY || @@ -1464,36 +1463,23 @@ GenTree* Compiler::impGetStructAddr(GenTree* structVal, } //------------------------------------------------------------------------ -// impNormStructType: Given a (known to be) struct class handle structHnd, normalize its type, -// and optionally determine the GC layout of the struct. +// impNormStructType: Given a (known to be) struct class handle structHnd and normalize its type. // // Arguments: // structHnd - The class handle for the struct type of interest. -// gcLayout - (optional, default nullptr) - a BYTE pointer, allocated by the caller, -// into which the gcLayout will be written. -// pNumGCVars - (optional, default nullptr) - if non-null, a pointer to an unsigned, -// which will be set to the number of GC fields in the struct. // pSimdBaseType - (optional, default nullptr) - if non-null, and the struct is a SIMD // type, set to the SIMD base type // // Return Value: // The JIT type for the struct (e.g. TYP_STRUCT, or TYP_SIMD*). -// The gcLayout will be returned using the pointers provided by the caller, if non-null. // It may also modify the compFloatingPointUsed flag if the type is a SIMD type. // -// Assumptions: -// The caller must set gcLayout to nullptr OR ensure that it is large enough -// (see ICorStaticInfo::getClassGClayout in corinfo.h). -// // Notes: // Normalizing the type involves examining the struct type to determine if it should // be modified to one that is handled specially by the JIT, possibly being a candidate // for full enregistration, e.g. TYP_SIMD16. -var_types Compiler::impNormStructType(CORINFO_CLASS_HANDLE structHnd, - BYTE* gcLayout, - unsigned* pNumGCVars, - var_types* pSimdBaseType) +var_types Compiler::impNormStructType(CORINFO_CLASS_HANDLE structHnd, var_types* pSimdBaseType) { assert(structHnd != NO_CLASS_HANDLE); @@ -1532,31 +1518,6 @@ var_types Compiler::impNormStructType(CORINFO_CLASS_HANDLE structHnd, } #endif // FEATURE_SIMD - // Fetch GC layout info if requested - if (gcLayout != nullptr) - { - unsigned numGCVars = info.compCompHnd->getClassGClayout(structHnd, gcLayout); - - // Verify that the quick test up above via the class attributes gave a - // safe view of the type's GCness. - // - // Note there are cases where mayContainGCPtrs is true but getClassGClayout - // does not report any gc fields. - - assert(mayContainGCPtrs || (numGCVars == 0)); - - if (pNumGCVars != nullptr) - { - *pNumGCVars = numGCVars; - } - } - else - { - // Can't safely ask for number of GC pointers without also - // asking for layout. - assert(pNumGCVars == nullptr); - } - return structType; } @@ -9421,7 +9382,6 @@ GenTree* Compiler::impFixupStructReturnType(GenTree* op, CORINFO_CLASS_HANDLE re op = op1->gtOp.gtOp1; goto REDO_RETURN_NODE; } - op->gtObj.gtClass = NO_CLASS_HANDLE; op->ChangeOperUnchecked(GT_IND); op->gtFlags |= GTF_IND_TGTANYWHERE; } @@ -15931,7 +15891,7 @@ void Compiler::impImportBlockCode(BasicBlock* block) if (op3->IsCnsIntOrI()) { size = (unsigned)op3->AsIntConCommon()->IconValue(); - op1 = new (this, GT_BLK) GenTreeBlk(GT_BLK, TYP_STRUCT, op1, size); + op1 = new (this, GT_BLK) GenTreeBlk(GT_BLK, TYP_STRUCT, op1, typGetBlkLayout(size)); } else { @@ -15955,7 +15915,7 @@ void Compiler::impImportBlockCode(BasicBlock* block) if (op3->IsCnsIntOrI()) { size = (unsigned)op3->AsIntConCommon()->IconValue(); - op1 = new (this, GT_BLK) GenTreeBlk(GT_BLK, TYP_STRUCT, op1, size); + op1 = new (this, GT_BLK) GenTreeBlk(GT_BLK, TYP_STRUCT, op1, typGetBlkLayout(size)); } else { diff --git a/src/jit/jit.h b/src/jit/jit.h index bdeccf46978c..697c21dfc731 100644 --- a/src/jit/jit.h +++ b/src/jit/jit.h @@ -489,7 +489,7 @@ typedef ptrdiff_t ssize_t; #define LOOP_HOIST_STATS 1 // Collect loop hoisting stats. #define TRACK_LSRA_STATS 1 // Collect LSRA stats #else -#define MEASURE_MEM_ALLOC 0 // You can set this to 1 to get memory stats in retail, as well +#define MEASURE_MEM_ALLOC 1 // You can set this to 1 to get memory stats in retail, as well #define LOOP_HOIST_STATS 0 // You can set this to 1 to get loop hoist stats in retail, as well #define TRACK_LSRA_STATS 0 // You can set this to 1 to get LSRA stats in retail, as well #endif diff --git a/src/jit/jithashtable.h b/src/jit/jithashtable.h index d411a2b87064..5ffb95604e32 100644 --- a/src/jit/jithashtable.h +++ b/src/jit/jithashtable.h @@ -95,6 +95,8 @@ class JitPrimeInfo // clang-format off SELECTANY const JitPrimeInfo jitPrimeInfo[] { + JitPrimeInfo(3, 0xaaaaaaab, 1), + JitPrimeInfo(5, 0xcccccccd, 2), JitPrimeInfo(9, 0x38e38e39, 1), JitPrimeInfo(23, 0xb21642c9, 4), JitPrimeInfo(59, 0x22b63cbf, 3), diff --git a/src/jit/lclvars.cpp b/src/jit/lclvars.cpp index e58130bd5bd1..4f308c491ddc 100644 --- a/src/jit/lclvars.cpp +++ b/src/jit/lclvars.cpp @@ -399,7 +399,7 @@ void Compiler::lvaInitThisPtr(InitVarDscInfo* varDscInfo) if (featureSIMD) { var_types simdBaseType = TYP_UNKNOWN; - var_types type = impNormStructType(info.compClassHnd, nullptr, nullptr, &simdBaseType); + var_types type = impNormStructType(info.compClassHnd, &simdBaseType); if (simdBaseType != TYP_UNKNOWN) { assert(varTypeIsSIMD(type)); @@ -1255,11 +1255,6 @@ void Compiler::lvaInitVarDsc(LclVarDsc* varDsc, varDsc->lvOverlappingFields = StructHasOverlappingFields(cFlags); } - if (varTypeIsGC(type)) - { - varDsc->lvStructGcCount = 1; - } - // Set the lvType (before this point it is TYP_UNDEF). if ((varTypeIsStruct(type))) { @@ -2448,41 +2443,15 @@ void Compiler::lvaSetStruct(unsigned varNum, CORINFO_CLASS_HANDLE typeHnd, bool } if (varDsc->lvExactSize == 0) { - BOOL isValueClass = info.compCompHnd->isValueClass(typeHnd); - - if (isValueClass) - { - varDsc->lvExactSize = info.compCompHnd->getClassSize(typeHnd); - } - else - { - varDsc->lvExactSize = info.compCompHnd->getHeapClassSize(typeHnd); - } - - size_t lvSize = varDsc->lvSize(); - assert((lvSize % TARGET_POINTER_SIZE) == - 0); // The struct needs to be a multiple of TARGET_POINTER_SIZE bytes for getClassGClayout() to be valid. - varDsc->lvGcLayout = getAllocator(CMK_LvaTable).allocate(lvSize / TARGET_POINTER_SIZE); - unsigned numGCVars; - var_types simdBaseType = TYP_UNKNOWN; - if (isValueClass) - { - varDsc->lvType = impNormStructType(typeHnd, varDsc->lvGcLayout, &numGCVars, &simdBaseType); - } - else - { - numGCVars = info.compCompHnd->getClassGClayout(typeHnd, varDsc->lvGcLayout); - } - - // We only save the count of GC vars in a struct up to 7. - if (numGCVars >= 8) - { - numGCVars = 7; - } - varDsc->lvStructGcCount = numGCVars; + StructTypeLayout* layout = typGetObjLayout(typeHnd); + layout->EnsureGCInfoInitialized(info.compCompHnd, getAllocator(CMK_GC)); + varDsc->lvLayout = layout; + varDsc->lvExactSize = layout->GetSize(); - if (isValueClass) + if (layout->IsValueClass()) { + var_types simdBaseType = TYP_UNKNOWN; + varDsc->lvType = impNormStructType(typeHnd, &simdBaseType); #if FEATURE_SIMD if (simdBaseType != TYP_UNKNOWN) { @@ -2502,7 +2471,7 @@ void Compiler::lvaSetStruct(unsigned varNum, CORINFO_CLASS_HANDLE typeHnd, bool varDsc->lvSetHfaTypeIsFloat(hfaType == TYP_FLOAT); // hfa variables can never contain GC pointers - assert(varDsc->lvStructGcCount == 0); + assert(!layout->HasGCPtr()); // The size of this struct should be evenly divisible by 4 or 8 assert((varDsc->lvExactSize % genTypeSize(hfaType)) == 0); // The number of elements in the HFA should fit into our MAX_ARG_REG_COUNT limit @@ -2786,17 +2755,6 @@ void Compiler::lvaUpdateClass(unsigned varNum, GenTree* tree, CORINFO_CLASS_HAND } } -/***************************************************************************** - * Returns the array of BYTEs containing the GC layout information - */ - -BYTE* Compiler::lvaGetGcLayout(unsigned varNum) -{ - assert(varTypeIsStruct(lvaTable[varNum].lvType) && (lvaTable[varNum].lvExactSize >= TARGET_POINTER_SIZE)); - - return lvaTable[varNum].lvGcLayout; -} - //------------------------------------------------------------------------ // lvaLclSize: returns size of a local variable, in bytes // @@ -3566,25 +3524,8 @@ var_types LclVarDsc::lvaArgType() type = TYP_INT; break; case 8: - switch (*lvGcLayout) - { - case TYPE_GC_NONE: - type = TYP_I_IMPL; - break; - - case TYPE_GC_REF: - type = TYP_REF; - break; - - case TYPE_GC_BYREF: - type = TYP_BYREF; - break; - - default: - unreached(); - } + type = Compiler::getJitGCType(lvLayout->GetGCPtr(0)); break; - default: type = TYP_BYREF; break; diff --git a/src/jit/lower.cpp b/src/jit/lower.cpp index e563609d3cca..65767fe7bfd4 100644 --- a/src/jit/lower.cpp +++ b/src/jit/lower.cpp @@ -1058,27 +1058,15 @@ GenTree* Lowering::NewPutArg(GenTreeCall* call, GenTree* arg, fgArgTabEntry* inf if (arg->OperGet() == GT_OBJ) { - BYTE* gcLayout = nullptr; - unsigned numRefs = 0; - GenTreeObj* argObj = arg->AsObj(); - - if (argObj->IsGCInfoInitialized()) - { - gcLayout = argObj->gtGcPtrs; - numRefs = argObj->GetGcPtrCount(); - } - else - { - // Set GC Pointer info - gcLayout = new (comp, CMK_Codegen) BYTE[info->numSlots + info->numRegs]; - numRefs = comp->info.compCompHnd->getClassGClayout(arg->gtObj.gtClass, gcLayout); - argSplit->setGcPointers(numRefs, gcLayout); - } + GenTreeObj* argObj = arg->AsObj(); + StructTypeLayout* layout = argObj->gtLayout; + layout->EnsureGCInfoInitialized(comp->info.compCompHnd, comp->getAllocator(CMK_GC)); + argSplit->setLayout(layout); // Set type of registers for (unsigned index = 0; index < info->numRegs; index++) { - var_types regType = comp->getJitGCType(gcLayout[index]); + var_types regType = Compiler::getJitGCType(layout->GetGCPtr(index)); // Account for the possibility that float fields may be passed in integer registers. if (varTypeIsFloating(regType) && !genIsValidFloatReg(argSplit->GetRegNumByIdx(index))) { @@ -1193,11 +1181,10 @@ GenTree* Lowering::NewPutArg(GenTreeCall* call, GenTree* arg, fgArgTabEntry* inf } else if (arg->OperIs(GT_OBJ)) { - unsigned numRefs = 0; - BYTE* gcLayout = new (comp, CMK_Codegen) BYTE[info->numSlots]; assert(!varTypeIsSIMD(arg)); - numRefs = comp->info.compCompHnd->getClassGClayout(arg->gtObj.gtClass, gcLayout); - putArg->AsPutArgStk()->setGcPointers(numRefs, gcLayout); + StructTypeLayout* layout = arg->AsObj()->gtLayout; + layout->EnsureGCInfoInitialized(comp->info.compCompHnd, comp->getAllocator(CMK_GC)); + putArg->AsPutArgStk()->setLayout(layout); #ifdef _TARGET_X86_ // On x86 VM lies about the type of a struct containing a pointer sized diff --git a/src/jit/lowerarmarch.cpp b/src/jit/lowerarmarch.cpp index d8d23eb0c282..92d50533d2c0 100644 --- a/src/jit/lowerarmarch.cpp +++ b/src/jit/lowerarmarch.cpp @@ -226,7 +226,7 @@ void Lowering::LowerStoreIndir(GenTreeIndir* node) void Lowering::LowerBlockStore(GenTreeBlk* blkNode) { GenTree* dstAddr = blkNode->Addr(); - unsigned size = blkNode->gtBlkSize; + unsigned size = blkNode->Size(); GenTree* source = blkNode->Data(); Compiler* compiler = comp; @@ -237,7 +237,8 @@ void Lowering::LowerBlockStore(GenTreeBlk* blkNode) if (!isInitBlk) { // CopyObj or CopyBlk - if ((blkNode->OperGet() == GT_STORE_OBJ) && ((blkNode->AsObj()->gtGcPtrCount == 0) || blkNode->gtBlkOpGcUnsafe)) + if ((blkNode->OperGet() == GT_STORE_OBJ) && + (!blkNode->AsObj()->gtLayout->HasGCPtr() || blkNode->gtBlkOpGcUnsafe)) { blkNode->SetOper(GT_STORE_BLK); } @@ -305,17 +306,16 @@ void Lowering::LowerBlockStore(GenTreeBlk* blkNode) // CopyObj GenTreeObj* objNode = blkNode->AsObj(); - unsigned slots = objNode->gtSlots; + unsigned slots = objNode->gtLayout->GetSlotCount(); #ifdef DEBUG // CpObj must always have at least one GC-Pointer as a member. - assert(objNode->gtGcPtrCount > 0); + assert(objNode->gtLayout->HasGCPtr()); assert(dstAddr->gtType == TYP_BYREF || dstAddr->gtType == TYP_I_IMPL); - CORINFO_CLASS_HANDLE clsHnd = objNode->gtClass; - size_t classSize = compiler->info.compCompHnd->getClassSize(clsHnd); - size_t blkSize = roundUp(classSize, TARGET_POINTER_SIZE); + size_t classSize = objNode->gtLayout->GetSize(); + size_t blkSize = roundUp(classSize, TARGET_POINTER_SIZE); // Currently, the EE always round up a class data structure so // we are not handling the case where we have a non multiple of pointer sized diff --git a/src/jit/lowerxarch.cpp b/src/jit/lowerxarch.cpp index cee84033cc7c..88aceb099905 100644 --- a/src/jit/lowerxarch.cpp +++ b/src/jit/lowerxarch.cpp @@ -146,7 +146,7 @@ void Lowering::LowerStoreIndir(GenTreeIndir* node) void Lowering::LowerBlockStore(GenTreeBlk* blkNode) { GenTree* dstAddr = blkNode->Addr(); - unsigned size = blkNode->gtBlkSize; + unsigned size = blkNode->Size(); GenTree* source = blkNode->Data(); Compiler* compiler = comp; GenTree* srcAddrOrFill = nullptr; @@ -155,7 +155,8 @@ void Lowering::LowerBlockStore(GenTreeBlk* blkNode) if (!isInitBlk) { // CopyObj or CopyBlk - if ((blkNode->OperGet() == GT_STORE_OBJ) && ((blkNode->AsObj()->gtGcPtrCount == 0) || blkNode->gtBlkOpGcUnsafe)) + if ((blkNode->OperGet() == GT_STORE_OBJ) && + (!blkNode->AsObj()->gtLayout->HasGCPtr() || blkNode->gtBlkOpGcUnsafe)) { blkNode->SetOper(GT_STORE_BLK); } @@ -248,15 +249,15 @@ void Lowering::LowerBlockStore(GenTreeBlk* blkNode) GenTreeObj* cpObjNode = blkNode->AsObj(); - unsigned slots = cpObjNode->gtSlots; + unsigned slots = cpObjNode->gtLayout->GetSlotCount(); #ifdef DEBUG // CpObj must always have at least one GC-Pointer as a member. - assert(cpObjNode->gtGcPtrCount > 0); + assert(cpObjNode->gtLayout->HasGCPtr()); assert(dstAddr->gtType == TYP_BYREF || dstAddr->gtType == TYP_I_IMPL); - CORINFO_CLASS_HANDLE clsHnd = cpObjNode->gtClass; + CORINFO_CLASS_HANDLE clsHnd = cpObjNode->gtLayout->GetClass(); size_t classSize = comp->info.compCompHnd->getClassSize(clsHnd); size_t blkSize = roundUp(classSize, TARGET_POINTER_SIZE); @@ -280,20 +281,20 @@ void Lowering::LowerBlockStore(GenTreeBlk* blkNode) // Let's inspect the struct/class layout and determine if it's profitable // to use rep movsq for copying non-gc memory instead of using single movsq // instructions for each memory slot. - unsigned i = 0; - BYTE* gcPtrs = cpObjNode->gtGcPtrs; + unsigned i = 0; + StructTypeLayout* layout = cpObjNode->gtLayout; do { unsigned nonGCSlots = 0; // Measure a contiguous non-gc area inside the struct and note the maximum. - while (i < slots && gcPtrs[i] == TYPE_GC_NONE) + while ((i < slots) && (layout->GetGCPtr(i) == TYPE_GC_NONE)) { nonGCSlots++; i++; } - while (i < slots && gcPtrs[i] != TYPE_GC_NONE) + while ((i < slots) && (layout->GetGCPtr(i) != TYPE_GC_NONE)) { i++; } diff --git a/src/jit/lsraarmarch.cpp b/src/jit/lsraarmarch.cpp index 611d858d6d8a..b066788b092b 100644 --- a/src/jit/lsraarmarch.cpp +++ b/src/jit/lsraarmarch.cpp @@ -570,7 +570,7 @@ int LinearScan::BuildPutArgSplit(GenTreePutArgSplit* argNode) int LinearScan::BuildBlockStore(GenTreeBlk* blkNode) { GenTree* dstAddr = blkNode->Addr(); - unsigned size = blkNode->gtBlkSize; + unsigned size = blkNode->Size(); GenTree* source = blkNode->Data(); int srcCount = 0; diff --git a/src/jit/lsrabuild.cpp b/src/jit/lsrabuild.cpp index 02464a1e8593..1d53c3469fe2 100644 --- a/src/jit/lsrabuild.cpp +++ b/src/jit/lsrabuild.cpp @@ -906,7 +906,7 @@ regMaskTP LinearScan::getKillSetForBlockStore(GenTreeBlk* blkNode) if ((blkNode->OperGet() == GT_STORE_OBJ) && blkNode->OperIsCopyBlkOp()) { - assert(blkNode->AsObj()->gtGcPtrCount != 0); + assert(blkNode->AsObj()->gtLayout->HasGCPtr()); killMask = compiler->compHelperCallKillSet(CORINFO_HELP_ASSIGN_BYREF); } else @@ -3121,7 +3121,7 @@ int LinearScan::BuildPutArgReg(GenTreeUnOp* node) { GenTreeObj* obj = op1->AsObj(); GenTree* addr = obj->Addr(); - unsigned size = obj->gtBlkSize; + unsigned size = obj->Size(); assert(size <= TARGET_POINTER_SIZE); if (addr->OperIsLocalAddr()) { diff --git a/src/jit/lsraxarch.cpp b/src/jit/lsraxarch.cpp index 6adb57404331..374801ea9664 100644 --- a/src/jit/lsraxarch.cpp +++ b/src/jit/lsraxarch.cpp @@ -1253,7 +1253,7 @@ int LinearScan::BuildCall(GenTreeCall* call) int LinearScan::BuildBlockStore(GenTreeBlk* blkNode) { GenTree* dstAddr = blkNode->Addr(); - unsigned size = blkNode->gtBlkSize; + unsigned size = blkNode->Size(); GenTree* source = blkNode->Data(); int srcCount = 0; diff --git a/src/jit/morph.cpp b/src/jit/morph.cpp index 09c6fc725853..9ff50ae7e5b7 100644 --- a/src/jit/morph.cpp +++ b/src/jit/morph.cpp @@ -1608,9 +1608,8 @@ void fgArgInfo::ArgsComplete() // else if (argx->OperGet() == GT_OBJ) { - GenTreeObj* argObj = argx->AsObj(); - CORINFO_CLASS_HANDLE objClass = argObj->gtClass; - unsigned structSize = compiler->info.compCompHnd->getClassSize(objClass); + GenTreeObj* argObj = argx->AsObj(); + unsigned structSize = argObj->gtLayout->GetSize(); switch (structSize) { case 3: @@ -3174,7 +3173,7 @@ void Compiler::fgInitArgInfo(GenTreeCall* call) { case GT_OBJ: // Get the size off the OBJ node. - structSize = actualArg->AsObj()->gtBlkSize; + structSize = actualArg->AsObj()->Size(); assert(structSize == info.compCompHnd->getClassSize(objClass)); break; case GT_LCL_VAR: @@ -3813,7 +3812,7 @@ GenTreeCall* Compiler::fgMorphArgs(GenTreeCall* call) if (argObj->OperIs(GT_OBJ)) { // Get the size off the OBJ node. - originalSize = argObj->AsObj()->gtBlkSize; + originalSize = argObj->AsObj()->Size(); assert(originalSize == info.compCompHnd->getClassSize(objClass)); } else @@ -4494,9 +4493,8 @@ GenTree* Compiler::fgMorphMultiregStructArg(GenTree* arg, fgArgTabEntry* fgEntry if (arg->OperGet() == GT_OBJ) { GenTreeObj* argObj = arg->AsObj(); - objClass = argObj->gtClass; - structSize = argObj->Size(); - assert(structSize == info.compCompHnd->getClassSize(objClass)); + objClass = argObj->gtLayout->GetClass(); + structSize = argObj->gtLayout->GetSize(); // If we have a GT_OBJ of a GT_ADDR then we set argValue to the child node of the GT_ADDR. GenTree* op1 = argObj->gtOp1; @@ -4676,7 +4674,7 @@ GenTree* Compiler::fgMorphMultiregStructArg(GenTree* arg, fgArgTabEntry* fgEntry for (unsigned inx = 0; inx < elemCount; inx++) { - CorInfoGCType currentGcLayoutType = (CorInfoGCType)varDsc->lvGcLayout[inx]; + CorInfoGCType currentGcLayoutType = (CorInfoGCType)varDsc->lvLayout->GetGCPtr(inx); // We setup the type[inx] value above using the GC info from 'objClass' // This GT_LCL_VAR must have the same GC layout info @@ -4837,21 +4835,21 @@ GenTree* Compiler::fgMorphMultiregStructArg(GenTree* arg, fgArgTabEntry* fgEntry // The allocated size of our LocalVar must be at least as big as lastOffset assert(varDsc->lvSize() >= lastOffset); - if (varDsc->lvStructGcCount > 0) + if (varDsc->lvLayout->HasGCPtr()) { // alignment of the baseOffset is required noway_assert((baseOffset % TARGET_POINTER_SIZE) == 0); #ifndef UNIX_AMD64_ABI noway_assert(elemSize == TARGET_POINTER_SIZE); #endif - unsigned baseIndex = baseOffset / TARGET_POINTER_SIZE; - const BYTE* gcPtrs = varDsc->lvGcLayout; // Get the GC layout for the local variable + unsigned baseIndex = baseOffset / TARGET_POINTER_SIZE; + StructTypeLayout* layout = varDsc->lvLayout; for (unsigned inx = 0; (inx < elemCount); inx++) { // The GC information must match what we setup using 'objClass' - if ((gcPtrs[baseIndex + inx] != TYPE_GC_NONE) || varTypeGCtype(type[inx])) + if ((layout->GetGCPtr(baseIndex + inx) != TYPE_GC_NONE) || varTypeGCtype(type[inx])) { - noway_assert(type[inx] == getJitGCType(gcPtrs[baseIndex + inx])); + noway_assert(type[inx] == getJitGCType(layout->GetGCPtr(baseIndex + inx))); } } } @@ -7036,7 +7034,7 @@ bool Compiler::fgCanFastTailCall(GenTreeCall* callee) if (argx->OperGet() == GT_OBJ) { - objClass = argx->AsObj()->gtClass; + objClass = argx->AsObj()->gtLayout->GetClass(); } else if (argx->IsLocal()) { @@ -7826,7 +7824,7 @@ void Compiler::fgMorphRecursiveFastTailCallIntoLoop(BasicBlock* block, GenTreeCa { var_types lclType = varDsc->TypeGet(); bool isUserLocal = (varNum < info.compLocalsCount); - bool structWithGCFields = ((lclType == TYP_STRUCT) && (varDsc->lvStructGcCount > 0)); + bool structWithGCFields = ((lclType == TYP_STRUCT) && varDsc->lvLayout->HasGCPtr()); if (isUserLocal || structWithGCFields) { GenTree* lcl = gtNewLclvNode(varNum, lclType); @@ -8934,7 +8932,7 @@ GenTree* Compiler::fgMorphOneAsgBlockOp(GenTree* tree) } if (lhsBlk->OperGet() == GT_OBJ) { - clsHnd = lhsBlk->AsObj()->gtClass; + clsHnd = lhsBlk->AsObj()->gtLayout->GetClass(); } } else @@ -9334,7 +9332,7 @@ GenTree* Compiler::fgMorphInitBlock(GenTree* tree) if (dest->OperIsBlk()) { destAddr = dest->AsBlk()->Addr(); - blockWidth = dest->AsBlk()->gtBlkSize; + blockWidth = dest->AsBlk()->Size(); } else { @@ -9717,7 +9715,7 @@ GenTree* Compiler::fgMorphBlkNode(GenTree* tree, bool isDest) } else { - tree = new (this, GT_BLK) GenTreeBlk(GT_BLK, structType, addr, genTypeSize(structType)); + tree = new (this, GT_BLK) GenTreeBlk(GT_BLK, structType, addr, typGetBlkLayout(genTypeSize(structType))); } gtUpdateNodeSideEffects(tree); @@ -9742,7 +9740,7 @@ GenTree* Compiler::fgMorphBlkNode(GenTree* tree, bool isDest) { blkNode->AsDynBlk()->gtDynamicSize = nullptr; blkNode->ChangeOper(GT_BLK); - blkNode->gtBlkSize = size; + blkNode->gtLayout = typGetBlkLayout(size); } else { @@ -9887,7 +9885,7 @@ GenTree* Compiler::fgMorphBlockOperand(GenTree* tree, var_types asgType, unsigne CORINFO_CLASS_HANDLE clsHnd = gtGetStructHandleIfPresent(effectiveVal); if (clsHnd == NO_CLASS_HANDLE) { - newTree = new (this, GT_BLK) GenTreeBlk(GT_BLK, TYP_STRUCT, addr, blockWidth); + newTree = new (this, GT_BLK) GenTreeBlk(GT_BLK, TYP_STRUCT, addr, typGetBlkLayout(blockWidth)); } else { @@ -9936,8 +9934,8 @@ GenTree* Compiler::fgMorphBlockOperand(GenTree* tree, var_types asgType, unsigne void Compiler::fgMorphUnsafeBlk(GenTreeObj* dest) { #if defined(CPBLK_UNROLL_LIMIT) && !defined(JIT32_GCENCODER) - assert(dest->gtGcPtrCount != 0); - unsigned blockWidth = dest->AsBlk()->gtBlkSize; + assert(dest->gtLayout->HasGCPtr()); + unsigned blockWidth = dest->AsBlk()->Size(); #ifdef DEBUG bool destOnStack = false; GenTree* destAddr = dest->Addr(); @@ -10059,7 +10057,7 @@ GenTree* Compiler::fgMorphCopyBlock(GenTree* tree) { blockWidth = genTypeSize(destLclVar->lvType); } - hasGCPtrs = destLclVar->lvStructGcCount != 0; + hasGCPtrs = lvaTypeIsGC(destLclNum); } else { @@ -10088,7 +10086,7 @@ GenTree* Compiler::fgMorphCopyBlock(GenTree* tree) assert(effectiveDest->OperIsBlk()); GenTreeBlk* blk = effectiveDest->AsBlk(); - blockWidth = blk->gtBlkSize; + blockWidth = blk->Size(); blockWidthIsConst = (blk->gtOper != GT_DYN_BLK); if ((dest == effectiveDest) && ((dest->gtFlags & GTF_IND_ARR_INDEX) == 0)) { @@ -18478,7 +18476,7 @@ class LocalAddressVisitor final : public GenTreeVisitor m_compiler->info.compCompHnd->getFieldClass(indir->AsField()->gtFldHnd)); case GT_BLK: case GT_OBJ: - return indir->AsBlk()->gtBlkSize; + return indir->AsBlk()->Size(); default: assert(indir->OperIs(GT_IND, GT_DYN_BLK)); return 0; diff --git a/src/jit/rationalize.cpp b/src/jit/rationalize.cpp index 407c9f832e73..71815de9ff43 100644 --- a/src/jit/rationalize.cpp +++ b/src/jit/rationalize.cpp @@ -366,37 +366,29 @@ void Rationalizer::RewriteAssignment(LIR::Use& use) { if ((location->OperGet() == GT_LCL_VAR)) { + LclVarDsc* varDsc = comp->lvaGetDesc(location->AsLclVar()); + // We need to construct a block node for the location. // Modify lcl to be the address form. location->SetOper(addrForm(locationOp)); - LclVarDsc* varDsc = &(comp->lvaTable[location->AsLclVarCommon()->gtLclNum]); - location->gtType = TYP_BYREF; - GenTreeBlk* storeBlk = nullptr; - unsigned int size = varDsc->lvExactSize; + location->gtType = TYP_BYREF; - if (varDsc->lvStructGcCount != 0) - { - CORINFO_CLASS_HANDLE structHnd = varDsc->lvVerTypeInfo.GetClassHandle(); - GenTreeObj* objNode = comp->gtNewObjNode(structHnd, location)->AsObj(); - unsigned int slots = roundUp(size, TARGET_POINTER_SIZE) / TARGET_POINTER_SIZE; - - objNode->SetGCInfo(varDsc->lvGcLayout, varDsc->lvStructGcCount, slots); - objNode->ChangeOper(GT_STORE_OBJ); - objNode->SetData(value); - comp->fgMorphUnsafeBlk(objNode); - storeBlk = objNode; - } - else + assignment->ChangeOper(varDsc->lvLayout->HasGCPtr() ? GT_STORE_OBJ : GT_STORE_BLK); + GenTreeBlk* store = assignment->AsBlk(); + + store->gtFlags |= GTF_IND_NONFAULTING; + store->gtFlags &= ~(GTF_GLOB_REF | GTF_REVERSE_OPS); + assert(store->Addr() == location); + assert(store->Data() == value); + store->gtLayout = varDsc->lvLayout; + store->gtBlkOpKind = GenTreeBlk::BlkOpKindInvalid; + store->gtBlkOpGcUnsafe = false; + + if (store->OperIs(GT_STORE_OBJ)) { - storeBlk = new (comp, GT_STORE_BLK) GenTreeBlk(GT_STORE_BLK, TYP_STRUCT, location, value, size); + comp->fgMorphUnsafeBlk(store->AsObj()); } - storeBlk->gtFlags |= GTF_ASG; - storeBlk->gtFlags |= ((location->gtFlags | value->gtFlags) & GTF_ALL_EFFECT); - GenTree* insertionPoint = location->gtNext; - BlockRange().InsertBefore(insertionPoint, storeBlk); - use.ReplaceWith(comp, storeBlk); - BlockRange().Remove(assignment); JITDUMP("After transforming local struct assignment into a block op:\n"); DISPTREERANGE(BlockRange(), use.Def()); JITDUMP("\n"); diff --git a/src/jit/simd.cpp b/src/jit/simd.cpp index a302c63e1e15..5d48a44b4b39 100644 --- a/src/jit/simd.cpp +++ b/src/jit/simd.cpp @@ -3103,18 +3103,18 @@ GenTree* Compiler::impSIMDIntrinsic(OPCODE opcode, GenTree* dupOp1 = fgInsertCommaFormTemp(&op1, gtGetStructHandleForSIMD(simdType, baseType)); // Widen the lower half and assign it to dstAddrLo. - simdTree = gtNewSIMDNode(simdType, op1, nullptr, SIMDIntrinsicWidenLo, baseType, size); - GenTree* loDest = - new (this, GT_BLK) GenTreeBlk(GT_BLK, simdType, dstAddrLo, getSIMDTypeSizeInBytes(clsHnd)); + simdTree = gtNewSIMDNode(simdType, op1, nullptr, SIMDIntrinsicWidenLo, baseType, size); + GenTree* loDest = new (this, GT_BLK) + GenTreeBlk(GT_BLK, simdType, dstAddrLo, typGetBlkLayout(getSIMDTypeSizeInBytes(clsHnd))); GenTree* loAsg = gtNewBlkOpNode(loDest, simdTree, getSIMDTypeSizeInBytes(clsHnd), false, // not volatile true); // copyBlock loAsg->gtFlags |= ((simdTree->gtFlags | dstAddrLo->gtFlags) & GTF_ALL_EFFECT); // Widen the upper half and assign it to dstAddrHi. - simdTree = gtNewSIMDNode(simdType, dupOp1, nullptr, SIMDIntrinsicWidenHi, baseType, size); - GenTree* hiDest = - new (this, GT_BLK) GenTreeBlk(GT_BLK, simdType, dstAddrHi, getSIMDTypeSizeInBytes(clsHnd)); + simdTree = gtNewSIMDNode(simdType, dupOp1, nullptr, SIMDIntrinsicWidenHi, baseType, size); + GenTree* hiDest = new (this, GT_BLK) + GenTreeBlk(GT_BLK, simdType, dstAddrHi, typGetBlkLayout(getSIMDTypeSizeInBytes(clsHnd))); GenTree* hiAsg = gtNewBlkOpNode(hiDest, simdTree, getSIMDTypeSizeInBytes(clsHnd), false, // not volatile true); // copyBlock @@ -3152,7 +3152,8 @@ GenTree* Compiler::impSIMDIntrinsic(OPCODE opcode, // block ops. if (doCopyBlk) { - GenTree* dest = new (this, GT_BLK) GenTreeBlk(GT_BLK, simdType, copyBlkDst, getSIMDTypeSizeInBytes(clsHnd)); + GenTree* dest = new (this, GT_BLK) + GenTreeBlk(GT_BLK, simdType, copyBlkDst, typGetBlkLayout(getSIMDTypeSizeInBytes(clsHnd))); dest->gtFlags |= GTF_GLOB_REF; retVal = gtNewBlkOpNode(dest, simdTree, getSIMDTypeSizeInBytes(clsHnd), false, // not volatile