From 1c2ccba34aa77e4073435aac32ce683dc8ffadfb Mon Sep 17 00:00:00 2001 From: Hanjoung Lee Date: Fri, 16 Jun 2017 09:53:44 +0900 Subject: [PATCH] [x86/Linux] Enable gcMarkFilterVarsPinned (WIN64EXCEPTIONS) (dotnet/coreclr#11281) * [x86/Linux] Enable gcMarkFilterVarsPinned Do gcMarkFilterVarsPinned() for WIN64EXCEPTIONS * [x86/Linux] GCInfo : Force this pointer untracked `this` pointer is now always untracked so we can use pinned flag in tracked lifetimes. This allows us to make the refs(inside filter) pinned to prevent from double-relocation. * [x86/Linux] GCInfo : fix comment and formatting * [x86/Linux] GCInfo : Update - Force "this" pointer untracked only when "this" is generic context - Style fixes * [x86/Linux] GCInfo : remove this_OFFSET_FLAG Commit migrated from https://github.com/dotnet/coreclr/commit/0c96db33ddb7d784cda9bd0368756046f436e8bc --- src/coreclr/src/gcdump/i386/gcdumpx86.cpp | 6 + src/coreclr/src/inc/gcinfo.h | 2 + src/coreclr/src/jit/codegenxarch.cpp | 9 + src/coreclr/src/jit/emit.cpp | 13 +- src/coreclr/src/jit/gcencode.cpp | 525 +++++++++++++++++------------- src/coreclr/src/jit/gcinfo.cpp | 4 + src/coreclr/src/jit/jitgcinfo.h | 17 +- src/coreclr/src/jit/lclvars.cpp | 8 + src/coreclr/src/vm/eetwain.cpp | 35 +- 9 files changed, 387 insertions(+), 232 deletions(-) diff --git a/src/coreclr/src/gcdump/i386/gcdumpx86.cpp b/src/coreclr/src/gcdump/i386/gcdumpx86.cpp index 9096085..a152feb 100644 --- a/src/coreclr/src/gcdump/i386/gcdumpx86.cpp +++ b/src/coreclr/src/gcdump/i386/gcdumpx86.cpp @@ -323,7 +323,11 @@ size_t GCDump::DumpGCTable(PTR_CBYTE table, gcPrintf("%s%s pointer\n", (lowBits & byref_OFFSET_FLAG) ? "byref " : "", +#ifndef WIN64EXCEPTIONS (lowBits & this_OFFSET_FLAG) ? "this" : "" +#else + (lowBits & pinned_OFFSET_FLAG) ? "pinned" : "" +#endif ); _ASSERTE(endOffs <= methodSize); @@ -677,7 +681,9 @@ size_t GCDump::DumpGCTable(PTR_CBYTE table, { argTab += decodeUnsigned(argTab, &val); +#ifndef WIN64EXCEPTIONS assert((val & this_OFFSET_FLAG) == 0); +#endif unsigned stkOffs = val & ~byref_OFFSET_FLAG; unsigned lowBit = val & byref_OFFSET_FLAG; diff --git a/src/coreclr/src/inc/gcinfo.h b/src/coreclr/src/inc/gcinfo.h index e5537e0..901f2cf 100644 --- a/src/coreclr/src/inc/gcinfo.h +++ b/src/coreclr/src/inc/gcinfo.h @@ -26,7 +26,9 @@ const unsigned OFFSET_MASK = 0x3; // mask to access the low 2 bits // const unsigned byref_OFFSET_FLAG = 0x1; // the offset is an interior ptr const unsigned pinned_OFFSET_FLAG = 0x2; // the offset is a pinned ptr +#if !defined(_TARGET_X86_) || !defined(WIN64EXCEPTIONS) const unsigned this_OFFSET_FLAG = 0x2; // the offset is "this" +#endif //----------------------------------------------------------------------------- // The current GCInfo Version diff --git a/src/coreclr/src/jit/codegenxarch.cpp b/src/coreclr/src/jit/codegenxarch.cpp index 4439e55..b7efccd 100644 --- a/src/coreclr/src/jit/codegenxarch.cpp +++ b/src/coreclr/src/jit/codegenxarch.cpp @@ -8223,6 +8223,15 @@ void* CodeGen::genCreateAndStoreGCInfoJIT32(unsigned codeSize, InfoHdr header; int s_cached; + +#ifdef WIN64EXCEPTIONS + // We should do this before gcInfoBlockHdrSave since varPtrTableSize must be finalized before it + if (compiler->ehAnyFunclets()) + { + gcInfo.gcMarkFilterVarsPinned(); + } +#endif + #ifdef DEBUG size_t headerSize = #endif diff --git a/src/coreclr/src/jit/emit.cpp b/src/coreclr/src/jit/emit.cpp index 4684230..bad0528 100644 --- a/src/coreclr/src/jit/emit.cpp +++ b/src/coreclr/src/jit/emit.cpp @@ -4721,12 +4721,14 @@ unsigned emitter::emitEndCodeGen(Compiler* comp, // printf("Variable #%2u/%2u is at stack offset %d\n", num, indx, offs); #ifdef JIT32_GCENCODER +#ifndef WIN64EXCEPTIONS /* Remember the frame offset of the "this" argument for synchronized methods */ if (emitComp->lvaIsOriginalThisArg(num) && emitComp->lvaKeepAliveAndReportThis()) { emitSyncThisObjOffs = offs; offs |= this_OFFSET_FLAG; } +#endif #endif // JIT32_GCENCODER if (dsc->TypeGet() == TYP_BYREF) @@ -5512,12 +5514,14 @@ void emitter::emitGCvarLiveSet(int offs, GCtype gcType, BYTE* addr, ssize_t disp desc->vpdNext = nullptr; +#if !defined(JIT32_GCENCODER) || !defined(WIN64EXCEPTIONS) /* the lower 2 bits encode props about the stk ptr */ if (offs == emitSyncThisObjOffs) { desc->vpdVarNum |= this_OFFSET_FLAG; } +#endif if (gcType == GCT_BYREF) { @@ -5604,10 +5608,17 @@ void emitter::emitGCvarDeadSet(int offs, BYTE* addr, ssize_t disp) if (EMITVERBOSE) { GCtype gcType = (desc->vpdVarNum & byref_OFFSET_FLAG) ? GCT_BYREF : GCT_GCREF; - bool isThis = (desc->vpdVarNum & this_OFFSET_FLAG) != 0; +#if !defined(JIT32_GCENCODER) || !defined(WIN64EXCEPTIONS) + bool isThis = (desc->vpdVarNum & this_OFFSET_FLAG) != 0; printf("[%08X] %s%s var died at [%s", dspPtr(desc), GCtypeStr(gcType), isThis ? "this-ptr" : "", emitGetFrameReg()); +#else + bool isPinned = (desc->vpdVarNum & pinned_OFFSET_FLAG) != 0; + + printf("[%08X] %s%s var died at [%s", dspPtr(desc), GCtypeStr(gcType), isPinned ? "pinned" : "", + emitGetFrameReg()); +#endif if (offs < 0) { diff --git a/src/coreclr/src/jit/gcencode.cpp b/src/coreclr/src/jit/gcencode.cpp index 4c300ac..a29da24 100644 --- a/src/coreclr/src/jit/gcencode.cpp +++ b/src/coreclr/src/jit/gcencode.cpp @@ -106,6 +106,299 @@ ReturnKind GCInfo::getReturnKind() } } +#if !defined(JIT32_GCENCODER) || defined(WIN64EXCEPTIONS) + +// gcMarkFilterVarsPinned - Walk all lifetimes and make it so that anything +// live in a filter is marked as pinned (often by splitting the lifetime +// so that *only* the filter region is pinned). This should only be +// called once (after generating all lifetimes, but before slot ids are +// finalized. +// +// DevDiv 376329 - The VM has to double report filters and their parent frame +// because they occur during the 1st pass and the parent frame doesn't go dead +// until we start unwinding in the 2nd pass. +// +// Untracked locals will only be reported in non-filter funclets and the +// parent. +// Registers can't be double reported by 2 frames since they're different. +// That just leaves stack variables which might be double reported. +// +// Technically double reporting is only a problem when the GC has to relocate a +// reference. So we avoid that problem by marking all live tracked stack +// variables as pinned inside the filter. Thus if they are double reported, it +// won't be a problem since they won't be double relocated. +// +void GCInfo::gcMarkFilterVarsPinned() +{ + assert(compiler->ehAnyFunclets()); + const EHblkDsc* endHBtab = &(compiler->compHndBBtab[compiler->compHndBBtabCount]); + + for (EHblkDsc* HBtab = compiler->compHndBBtab; HBtab < endHBtab; HBtab++) + { + if (HBtab->HasFilter()) + { + const UNATIVE_OFFSET filterBeg = compiler->ehCodeOffset(HBtab->ebdFilter); + const UNATIVE_OFFSET filterEnd = compiler->ehCodeOffset(HBtab->ebdHndBeg); + + for (varPtrDsc* varTmp = gcVarPtrList; varTmp != nullptr; varTmp = varTmp->vpdNext) + { + // Get hold of the variable's flags. + const unsigned lowBits = varTmp->vpdVarNum & OFFSET_MASK; + + // Compute the actual lifetime offsets. + const unsigned begOffs = varTmp->vpdBegOfs; + const unsigned endOffs = varTmp->vpdEndOfs; + + // Special case: skip any 0-length lifetimes. + if (endOffs == begOffs) + { + continue; + } + + // Skip lifetimes with no overlap with the filter + if ((endOffs <= filterBeg) || (begOffs >= filterEnd)) + { + continue; + } + +#ifndef JIT32_GCENCODER + // Because there is no nesting within filters, nothing + // should be already pinned. + // For JIT32_GCENCODER, we should not do this check as gcVarPtrList are always sorted by vpdBegOfs + // which means that we could see some varPtrDsc that were already pinned by previous splitting. + assert((lowBits & pinned_OFFSET_FLAG) == 0); +#endif // JIT32_GCENCODER + + if (begOffs < filterBeg) + { + if (endOffs > filterEnd) + { + // The variable lifetime is starts before AND ends after + // the filter, so we need to create 2 new lifetimes: + // (1) a pinned one for the filter + // (2) a regular one for after the filter + // and then adjust the original lifetime to end before + // the filter. + CLANG_FORMAT_COMMENT_ANCHOR; + +#ifdef DEBUG + if (compiler->verbose) + { + printf("Splitting lifetime for filter: [%04X, %04X).\nOld: ", filterBeg, filterEnd); + gcDumpVarPtrDsc(varTmp); + } +#endif // DEBUG + + varPtrDsc* desc1 = new (compiler, CMK_GC) varPtrDsc; + desc1->vpdVarNum = varTmp->vpdVarNum | pinned_OFFSET_FLAG; + desc1->vpdBegOfs = filterBeg; + desc1->vpdEndOfs = filterEnd; + + varPtrDsc* desc2 = new (compiler, CMK_GC) varPtrDsc; + desc2->vpdVarNum = varTmp->vpdVarNum; + desc2->vpdBegOfs = filterEnd; + desc2->vpdEndOfs = endOffs; + + varTmp->vpdEndOfs = filterBeg; + + gcInsertVarPtrDscSplit(desc1, varTmp); + gcInsertVarPtrDscSplit(desc2, varTmp); + +#ifdef DEBUG + if (compiler->verbose) + { + printf("New (1 of 3): "); + gcDumpVarPtrDsc(varTmp); + printf("New (2 of 3): "); + gcDumpVarPtrDsc(desc1); + printf("New (3 of 3): "); + gcDumpVarPtrDsc(desc2); + } +#endif // DEBUG + } + else + { + // The variable lifetime started before the filter and ends + // somewhere inside it, so we only create 1 new lifetime, + // and then adjust the original lifetime to end before + // the filter. + CLANG_FORMAT_COMMENT_ANCHOR; + +#ifdef DEBUG + if (compiler->verbose) + { + printf("Splitting lifetime for filter.\nOld: "); + gcDumpVarPtrDsc(varTmp); + } +#endif // DEBUG + + varPtrDsc* desc = new (compiler, CMK_GC) varPtrDsc; + desc->vpdVarNum = varTmp->vpdVarNum | pinned_OFFSET_FLAG; + desc->vpdBegOfs = filterBeg; + desc->vpdEndOfs = endOffs; + + varTmp->vpdEndOfs = filterBeg; + + gcInsertVarPtrDscSplit(desc, varTmp); + +#ifdef DEBUG + if (compiler->verbose) + { + printf("New (1 of 2): "); + gcDumpVarPtrDsc(varTmp); + printf("New (2 of 2): "); + gcDumpVarPtrDsc(desc); + } +#endif // DEBUG + } + } + else + { + if (endOffs > filterEnd) + { + // The variable lifetime starts inside the filter and + // ends somewhere after it, so we create 1 new + // lifetime for the part inside the filter and adjust + // the start of the original lifetime to be the end + // of the filter + CLANG_FORMAT_COMMENT_ANCHOR; +#ifdef DEBUG + if (compiler->verbose) + { + printf("Splitting lifetime for filter.\nOld: "); + gcDumpVarPtrDsc(varTmp); + } +#endif // DEBUG + + varPtrDsc* desc = new (compiler, CMK_GC) varPtrDsc; +#ifndef JIT32_GCENCODER + desc->vpdVarNum = varTmp->vpdVarNum | pinned_OFFSET_FLAG; + desc->vpdBegOfs = begOffs; + desc->vpdEndOfs = filterEnd; + + varTmp->vpdBegOfs = filterEnd; +#else + // Mark varTmp as pinned and generated use varPtrDsc(desc) as non-pinned + // since gcInsertVarPtrDscSplit requires that varTmp->vpdBegOfs must precede desc->vpdBegOfs + desc->vpdVarNum = varTmp->vpdVarNum; + desc->vpdBegOfs = filterEnd; + desc->vpdEndOfs = endOffs; + + varTmp->vpdVarNum = varTmp->vpdVarNum | pinned_OFFSET_FLAG; + varTmp->vpdEndOfs = filterEnd; +#endif + + gcInsertVarPtrDscSplit(desc, varTmp); + +#ifdef DEBUG + if (compiler->verbose) + { + printf("New (1 of 2): "); + gcDumpVarPtrDsc(desc); + printf("New (2 of 2): "); + gcDumpVarPtrDsc(varTmp); + } +#endif // DEBUG + } + else + { + // The variable lifetime is completely within the filter, + // so just add the pinned flag. + CLANG_FORMAT_COMMENT_ANCHOR; +#ifdef DEBUG + if (compiler->verbose) + { + printf("Pinning lifetime for filter.\nOld: "); + gcDumpVarPtrDsc(varTmp); + } +#endif // DEBUG + + varTmp->vpdVarNum |= pinned_OFFSET_FLAG; +#ifdef DEBUG + if (compiler->verbose) + { + printf("New : "); + gcDumpVarPtrDsc(varTmp); + } +#endif // DEBUG + } + } + } + } // HasFilter + } // Foreach EH +} + +// gcInsertVarPtrDscSplit - Insert varPtrDsc that were created by splitting lifetimes +// From gcMarkFilterVarsPinned, we may have created one or two `varPtrDsc`s due to splitting lifetimes +// and these newly created `varPtrDsc`s should be inserted in gcVarPtrList. +// However the semantics of this call depend on the architecture. +// +// x86-GCInfo requires gcVarPtrList to be sorted by vpdBegOfs. +// Every time inserting an entry we should keep the order of entries. +// So this function searches for a proper insertion point from "begin" then "desc" gets inserted. +// +// For other architectures(ones that uses GCInfo{En|De}coder), we don't need any sort. +// So the argument "begin" is unused and "desc" will be inserted at the front of the list. + +void GCInfo::gcInsertVarPtrDscSplit(varPtrDsc* desc, varPtrDsc* begin) +{ +#ifndef JIT32_GCENCODER + (void)begin; + desc->vpdNext = gcVarPtrList; + gcVarPtrList = desc; +#else // JIT32_GCENCODER + // "desc" and "begin" must not be null + assert(desc != nullptr); + assert(begin != nullptr); + + // The caller must guarantee that desc's BegOfs is equal or greater than begin's + // since we will search for insertion point from "begin" + assert(desc->vpdBegOfs >= begin->vpdBegOfs); + + varPtrDsc* varTmp = begin->vpdNext; + varPtrDsc* varInsert = begin; + + while (varTmp != nullptr && varTmp->vpdBegOfs < desc->vpdBegOfs) + { + varInsert = varTmp; + varTmp = varTmp->vpdNext; + } + + // Insert point cannot be null + assert(varInsert != nullptr); + + desc->vpdNext = varInsert->vpdNext; + varInsert->vpdNext = desc; +#endif // JIT32_GCENCODER +} + +#ifdef DEBUG + +void GCInfo::gcDumpVarPtrDsc(varPtrDsc* desc) +{ + const int offs = (desc->vpdVarNum & ~OFFSET_MASK); + const GCtype gcType = (desc->vpdVarNum & byref_OFFSET_FLAG) ? GCT_BYREF : GCT_GCREF; + const bool isPin = (desc->vpdVarNum & pinned_OFFSET_FLAG) != 0; + + printf("[%08X] %s%s var at [%s", dspPtr(desc), GCtypeStr(gcType), isPin ? "pinned-ptr" : "", + compiler->isFramePointerUsed() ? STR_FPBASE : STR_SPBASE); + + if (offs < 0) + { + printf("-%02XH", -offs); + } + else if (offs > 0) + { + printf("+%02XH", +offs); + } + + printf("] live from %04X to %04X\n", desc->vpdBegOfs, desc->vpdEndOfs); +} + +#endif // DEBUG + +#endif // !defined(JIT32_GCENCODER) || defined(WIN64EXCEPTIONS) + #ifdef JIT32_GCENCODER #include "emit.h" @@ -1972,7 +2265,11 @@ size_t GCInfo::gcMakeRegPtrTable(BYTE* dest, int mask, const InfoHdr& header, un } } +#ifndef WIN64EXCEPTIONS + // For WIN64EXCEPTIONS, "this" must always be in untracked variables + // so we cannot have "this" in variable lifetimes if (compiler->lvaIsOriginalThisArg(varNum) && compiler->lvaKeepAliveAndReportThis()) + { // Encoding of untracked variables does not support reporting // "this". So report it as a tracked variable with a liveness @@ -1981,6 +2278,7 @@ size_t GCInfo::gcMakeRegPtrTable(BYTE* dest, int mask, const InfoHdr& header, un thisKeptAliveIsInUntracked = true; continue; } +#endif if (pass == 0) count++; @@ -2140,6 +2438,7 @@ size_t GCInfo::gcMakeRegPtrTable(BYTE* dest, int mask, const InfoHdr& header, un varPtrDsc* varTmp; count = 0; +#ifndef WIN64EXCEPTIONS if (thisKeptAliveIsInUntracked) { count = 1; @@ -2166,6 +2465,7 @@ size_t GCInfo::gcMakeRegPtrTable(BYTE* dest, int mask, const InfoHdr& header, un dest += (sz & mask); totalSize += sz; } +#endif for (pass = 0; pass < 2; pass++) { @@ -3452,27 +3752,6 @@ template class SimplerHashTablevpdVarNum & ~OFFSET_MASK); - const GCtype gcType = (desc->vpdVarNum & byref_OFFSET_FLAG) ? GCT_BYREF : GCT_GCREF; - const bool isPin = (desc->vpdVarNum & pinned_OFFSET_FLAG) != 0; - - printf("[%08X] %s%s var at [%s", dspPtr(desc), GCtypeStr(gcType), isPin ? "pinned-ptr" : "", - compiler->isFramePointerUsed() ? STR_FPBASE : STR_SPBASE); - - if (offs < 0) - { - printf("-%02XH", -offs); - } - else if (offs > 0) - { - printf("+%02XH", +offs); - } - - printf("] live from %04X to %04X\n", desc->vpdBegOfs, desc->vpdEndOfs); -} - static const char* const GcSlotFlagsNames[] = {"", "(byref) ", "(pinned) ", @@ -4560,210 +4839,6 @@ void GCInfo::gcMakeVarPtrTable(GcInfoEncoder* gcInfoEncoder, MakeRegPtrMode mode } } -// gcMarkFilterVarsPinned - Walk all lifetimes and make it so that anything -// live in a filter is marked as pinned (often by splitting the lifetime -// so that *only* the filter region is pinned). This should only be -// called once (after generating all lifetimes, but before slot ids are -// finalized. -// -// DevDiv 376329 - The VM has to double report filters and their parent frame -// because they occur during the 1st pass and the parent frame doesn't go dead -// until we start unwinding in the 2nd pass. -// -// Untracked locals will only be reported in non-filter funclets and the -// parent. -// Registers can't be double reported by 2 frames since they're different. -// That just leaves stack variables which might be double reported. -// -// Technically double reporting is only a problem when the GC has to relocate a -// reference. So we avoid that problem by marking all live tracked stack -// variables as pinned inside the filter. Thus if they are double reported, it -// won't be a problem since they won't be double relocated. -// -void GCInfo::gcMarkFilterVarsPinned() -{ - assert(compiler->ehAnyFunclets()); - const EHblkDsc* endHBtab = &(compiler->compHndBBtab[compiler->compHndBBtabCount]); - - for (EHblkDsc* HBtab = compiler->compHndBBtab; HBtab < endHBtab; HBtab++) - { - if (HBtab->HasFilter()) - { - const UNATIVE_OFFSET filterBeg = compiler->ehCodeOffset(HBtab->ebdFilter); - const UNATIVE_OFFSET filterEnd = compiler->ehCodeOffset(HBtab->ebdHndBeg); - - for (varPtrDsc* varTmp = gcVarPtrList; varTmp != nullptr; varTmp = varTmp->vpdNext) - { - // Get hold of the variable's flags. - const unsigned lowBits = varTmp->vpdVarNum & OFFSET_MASK; - - // Compute the actual lifetime offsets. - const unsigned begOffs = varTmp->vpdBegOfs; - const unsigned endOffs = varTmp->vpdEndOfs; - - // Special case: skip any 0-length lifetimes. - if (endOffs == begOffs) - { - continue; - } - - // Skip lifetimes with no overlap with the filter - if ((endOffs <= filterBeg) || (begOffs >= filterEnd)) - { - continue; - } - - // Because there is no nesting within filters, nothing - // should be already pinned. - assert((lowBits & pinned_OFFSET_FLAG) == 0); - - if (begOffs < filterBeg) - { - if (endOffs > filterEnd) - { - // The variable lifetime is starts before AND ends after - // the filter, so we need to create 2 new lifetimes: - // (1) a pinned one for the filter - // (2) a regular one for after the filter - // and then adjust the original lifetime to end before - // the filter. - CLANG_FORMAT_COMMENT_ANCHOR; - -#ifdef DEBUG - if (compiler->verbose) - { - printf("Splitting lifetime for filter: [%04X, %04X).\nOld: ", filterBeg, filterEnd); - gcDumpVarPtrDsc(varTmp); - } -#endif // DEBUG - - varPtrDsc* desc1 = new (compiler, CMK_GC) varPtrDsc; - desc1->vpdNext = gcVarPtrList; - desc1->vpdVarNum = varTmp->vpdVarNum | pinned_OFFSET_FLAG; - desc1->vpdBegOfs = filterBeg; - desc1->vpdEndOfs = filterEnd; - - varPtrDsc* desc2 = new (compiler, CMK_GC) varPtrDsc; - desc2->vpdNext = desc1; - desc2->vpdVarNum = varTmp->vpdVarNum; - desc2->vpdBegOfs = filterEnd; - desc2->vpdEndOfs = endOffs; - gcVarPtrList = desc2; - - varTmp->vpdEndOfs = filterBeg; -#ifdef DEBUG - if (compiler->verbose) - { - printf("New (1 of 3): "); - gcDumpVarPtrDsc(varTmp); - printf("New (2 of 3): "); - gcDumpVarPtrDsc(desc1); - printf("New (3 of 3): "); - gcDumpVarPtrDsc(desc2); - } -#endif // DEBUG - } - else - { - // The variable lifetime started before the filter and ends - // somewhere inside it, so we only create 1 new lifetime, - // and then adjust the original lifetime to end before - // the filter. - CLANG_FORMAT_COMMENT_ANCHOR; - -#ifdef DEBUG - if (compiler->verbose) - { - printf("Splitting lifetime for filter.\nOld: "); - gcDumpVarPtrDsc(varTmp); - } -#endif // DEBUG - - varPtrDsc* desc = new (compiler, CMK_GC) varPtrDsc; - desc->vpdNext = gcVarPtrList; - desc->vpdVarNum = varTmp->vpdVarNum | pinned_OFFSET_FLAG; - desc->vpdBegOfs = filterBeg; - desc->vpdEndOfs = endOffs; - gcVarPtrList = desc; - - varTmp->vpdEndOfs = filterBeg; - -#ifdef DEBUG - if (compiler->verbose) - { - printf("New (1 of 2): "); - gcDumpVarPtrDsc(varTmp); - printf("New (2 of 2): "); - gcDumpVarPtrDsc(desc); - } -#endif // DEBUG - } - } - else - { - if (endOffs > filterEnd) - { - // The variable lifetime starts inside the filter and - // ends somewhere after it, so we create 1 new - // lifetime for the part inside the filter and adjust - // the start of the original lifetime to be the end - // of the filter - CLANG_FORMAT_COMMENT_ANCHOR; -#ifdef DEBUG - if (compiler->verbose) - { - printf("Splitting lifetime for filter.\nOld: "); - gcDumpVarPtrDsc(varTmp); - } -#endif // DEBUG - - varPtrDsc* desc = new (compiler, CMK_GC) varPtrDsc; - desc->vpdNext = gcVarPtrList; - desc->vpdVarNum = varTmp->vpdVarNum | pinned_OFFSET_FLAG; - desc->vpdBegOfs = begOffs; - desc->vpdEndOfs = filterEnd; - gcVarPtrList = desc; - - varTmp->vpdBegOfs = filterEnd; - -#ifdef DEBUG - if (compiler->verbose) - { - printf("New (1 of 2): "); - gcDumpVarPtrDsc(desc); - printf("New (2 of 2): "); - gcDumpVarPtrDsc(varTmp); - } -#endif // DEBUG - } - else - { - // The variable lifetime is completely within the filter, - // so just add the pinned flag. - CLANG_FORMAT_COMMENT_ANCHOR; -#ifdef DEBUG - if (compiler->verbose) - { - printf("Pinning lifetime for filter.\nOld: "); - gcDumpVarPtrDsc(varTmp); - } -#endif // DEBUG - - varTmp->vpdVarNum |= pinned_OFFSET_FLAG; -#ifdef DEBUG - if (compiler->verbose) - { - printf("New : "); - gcDumpVarPtrDsc(varTmp); - } -#endif // DEBUG - } - } - } - } // HasFilter - } // Foreach EH -} - void GCInfo::gcInfoRecordGCStackArgLive(GcInfoEncoder* gcInfoEncoder, MakeRegPtrMode mode, regPtrDsc* genStackPtr) { // On non-x86 platforms, don't have pointer argument push/pop/kill declarations. diff --git a/src/coreclr/src/jit/gcinfo.cpp b/src/coreclr/src/jit/gcinfo.cpp index ece039e..293abd5 100644 --- a/src/coreclr/src/jit/gcinfo.cpp +++ b/src/coreclr/src/jit/gcinfo.cpp @@ -476,6 +476,9 @@ void GCInfo::gcCountForHeader(UNALIGNED unsigned int* untrackedCount, UNALIGNED } } +#if !defined(JIT32_GCENCODER) || !defined(WIN64EXCEPTIONS) + // For x86/WIN64EXCEPTIONS, "this" must always be in untracked variables + // so we cannot have "this" in variable lifetimes if (compiler->lvaIsOriginalThisArg(varNum) && compiler->lvaKeepAliveAndReportThis()) { // Encoding of untracked variables does not support reporting @@ -485,6 +488,7 @@ void GCInfo::gcCountForHeader(UNALIGNED unsigned int* untrackedCount, UNALIGNED thisKeptAliveIsInUntracked = true; continue; } +#endif #ifdef DEBUG if (compiler->verbose) diff --git a/src/coreclr/src/jit/jitgcinfo.h b/src/coreclr/src/jit/jitgcinfo.h index 7b17b84..b2a8eb0 100644 --- a/src/coreclr/src/jit/jitgcinfo.h +++ b/src/coreclr/src/jit/jitgcinfo.h @@ -208,10 +208,6 @@ public: // In the "do work" mode, we use these slot ids to actually declare live ranges to the encoder. void gcMakeVarPtrTable(GcInfoEncoder* gcInfoEncoder, MakeRegPtrMode mode); - // This method expands the tracked stack variables lifetimes so that any lifetimes within filters - // are reported as pinned. - void gcMarkFilterVarsPinned(); - // At instruction offset "instrOffset," the set of registers indicated by "regMask" is becoming live or dead, // depending on whether "newState" is "GC_SLOT_DEAD" or "GC_SLOT_LIVE". The subset of registers whose corresponding // bits are set in "byRefMask" contain by-refs rather than regular GC pointers. "*pPtrRegs" is the set of @@ -359,11 +355,22 @@ private: #else // JIT32_GCENCODER void gcInfoBlockHdrSave(GcInfoEncoder* gcInfoEncoder, unsigned methodSize, unsigned prologSize); +#endif // JIT32_GCENCODER + +#if !defined(JIT32_GCENCODER) || defined(WIN64EXCEPTIONS) + + // This method expands the tracked stack variables lifetimes so that any lifetimes within filters + // are reported as pinned. + void gcMarkFilterVarsPinned(); + + // Insert a varPtrDsc to gcVarPtrList that was generated by splitting lifetimes + void gcInsertVarPtrDscSplit(varPtrDsc* desc, varPtrDsc* begin); + #ifdef DEBUG void gcDumpVarPtrDsc(varPtrDsc* desc); #endif // DEBUG -#endif // JIT32_GCENCODER +#endif // !defined(JIT32_GCENCODER) || defined(WIN64EXCEPTIONS) #if DUMP_GC_TABLES diff --git a/src/coreclr/src/jit/lclvars.cpp b/src/coreclr/src/jit/lclvars.cpp index 0d557de..eda815d 100644 --- a/src/coreclr/src/jit/lclvars.cpp +++ b/src/coreclr/src/jit/lclvars.cpp @@ -3293,6 +3293,14 @@ void Compiler::lvaSortByRefCount() { varDsc->lvTracked = 0; } +#if defined(JIT32_GCENCODER) && defined(WIN64EXCEPTIONS) + else if (lvaIsOriginalThisArg(lclNum) && (info.compMethodInfo->options & CORINFO_GENERICS_CTXT_FROM_THIS) != 0) + { + // For x86/Linux, we need to track "this". + // However we cannot have it in tracked variables, so we set "this" pointer always untracked + varDsc->lvTracked = 0; + } +#endif // Are we not optimizing and we have exception handlers? // if so mark all args and locals "do not enregister". diff --git a/src/coreclr/src/vm/eetwain.cpp b/src/coreclr/src/vm/eetwain.cpp index 99e9107..9fe9670 100644 --- a/src/coreclr/src/vm/eetwain.cpp +++ b/src/coreclr/src/vm/eetwain.cpp @@ -4796,7 +4796,12 @@ bool EECodeManager::EnumGcRefs( PREGDISPLAY pContext, if (dspPtr) { printf(" Frame %s%s local at [E", (lowBits & byref_OFFSET_FLAG) ? "byref " : "", +#ifndef WIN64EXCEPTIONS (lowBits & this_OFFSET_FLAG) ? "this-ptr" : ""); +#else + (lowBits & pinned_OFFSET_FLAG) ? "pinned" : ""); +#endif + int dspOffs = ptrAddr; char frameType; @@ -4816,8 +4821,22 @@ bool EECodeManager::EnumGcRefs( PREGDISPLAY pContext, printf("%cP+%02XH]: ", frameType, +dspOffs); } #endif + + unsigned flags = CHECK_APP_DOMAIN; +#ifndef WIN64EXCEPTIONS + // First Bit : byref + // Second Bit : this + // The second bit means `this` not `pinned`. So we ignore it. + flags |= lowBits & byref_OFFSET_FLAG; +#else + // First Bit : byref + // Second Bit : pinned + // Both bits are valid + flags |= lowBits; +#endif + _ASSERTE(byref_OFFSET_FLAG == GC_CALL_INTERIOR); - pCallBack(hCallBack, (OBJECTREF*)(size_t)ptrAddr, (lowBits & byref_OFFSET_FLAG) | CHECK_APP_DOMAIN + pCallBack(hCallBack, (OBJECTREF*)(size_t)ptrAddr, flags DAC_ARG(DacSlotLocation(info.ebpFrame ? REGI_EBP : REGI_ESP, info.ebpFrame ? EBP - ptrAddr : ptrAddr - ESP, true))); @@ -5310,6 +5329,7 @@ OBJECTREF EECodeManager::GetInstance( PREGDISPLAY pContext, _ASSERTE(*castto(table, unsigned short *)++ == 0xBEEF); #endif +#ifndef WIN64EXCEPTIONS /* Parse the untracked frame variable table */ /* The 'this' pointer can never be located in the untracked table */ @@ -5356,6 +5376,19 @@ OBJECTREF EECodeManager::GetInstance( PREGDISPLAY pContext, _ASSERTE(*castto(table, unsigned short *) == 0xBABE); #endif +#else // WIN64EXCEPTIONS + if (pCodeInfo->GetMethodDesc()->AcquiresInstMethodTableFromThis()) // Generic Context is "this" + { + // Untracked table must have at least one entry - this pointer + _ASSERTE(info.untrackedCnt > 0); + + // The first entry must be "this" pointer + int stkOffs = fastDecodeSigned(table); + taArgBase -= stkOffs & ~OFFSET_MASK; + return (OBJECTREF)(size_t)(*PTR_DWORD(taArgBase)); + } +#endif // WIN64EXCEPTIONS + return NULL; #else // !USE_GC_INFO_DECODER PTR_VOID token = EECodeManager::GetExactGenericsToken(pContext, pCodeInfo); -- 2.7.4