diff --git a/src/coreclr/System.Private.CoreLib/src/System/Runtime/ExceptionServices/AsmOffsets.cs b/src/coreclr/System.Private.CoreLib/src/System/Runtime/ExceptionServices/AsmOffsets.cs index e1bc5e6a094c4c..df3da7a876b787 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Runtime/ExceptionServices/AsmOffsets.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Runtime/ExceptionServices/AsmOffsets.cs @@ -68,9 +68,9 @@ class AsmOffsets public const int OFFSETOF__StackFrameIterator__m_isRuntimeWrappedExceptions = 0x132; #elif TARGET_X86 public const int OFFSETOF__REGDISPLAY__m_pCurrentContext = 0x4; - public const int SIZEOF__StackFrameIterator = 0x3cc; - public const int OFFSETOF__StackFrameIterator__m_isRuntimeWrappedExceptions = 0x3ba; - public const int OFFSETOF__StackFrameIterator__m_AdjustedControlPC = 0x3c8; + public const int SIZEOF__StackFrameIterator = 0x3d4; + public const int OFFSETOF__StackFrameIterator__m_isRuntimeWrappedExceptions = 0x3c2; + public const int OFFSETOF__StackFrameIterator__m_AdjustedControlPC = 0x3d0; #else // TARGET_64BIT public const int OFFSETOF__REGDISPLAY__m_pCurrentContext = 0x4; public const int SIZEOF__StackFrameIterator = 0xcc; @@ -134,9 +134,9 @@ class AsmOffsets public const int OFFSETOF__StackFrameIterator__m_isRuntimeWrappedExceptions = 0x12a; #elif TARGET_X86 public const int OFFSETOF__REGDISPLAY__m_pCurrentContext = 0x4; - public const int SIZEOF__StackFrameIterator = 0x3c4; - public const int OFFSETOF__StackFrameIterator__m_isRuntimeWrappedExceptions = 0x3b2; - public const int OFFSETOF__StackFrameIterator__m_AdjustedControlPC = 0x3c0; + public const int SIZEOF__StackFrameIterator = 0x3cc; + public const int OFFSETOF__StackFrameIterator__m_isRuntimeWrappedExceptions = 0x3ba; + public const int OFFSETOF__StackFrameIterator__m_AdjustedControlPC = 0x3c8; #else // TARGET_64BIT public const int OFFSETOF__REGDISPLAY__m_pCurrentContext = 0x4; public const int SIZEOF__StackFrameIterator = 0xc4; diff --git a/src/coreclr/gcdump/i386/gcdumpx86.cpp b/src/coreclr/gcdump/i386/gcdumpx86.cpp index c035f8ac64b2f9..bb85705b45c82a 100644 --- a/src/coreclr/gcdump/i386/gcdumpx86.cpp +++ b/src/coreclr/gcdump/i386/gcdumpx86.cpp @@ -114,6 +114,17 @@ size_t GCDump::DumpInfoHdr (PTR_CBYTE gcInfoBlock, header->revPInvokeOffset = count; } + if (header->noGCRegionCnt == HAS_NOGCREGIONS) + { + hasArgTabOffset = TRUE; + table += decodeUnsigned(table, &count); + header->noGCRegionCnt = count; + } + else if (header->noGCRegionCnt > 0) + { + hasArgTabOffset = TRUE; + } + // // First print out all the basic information // @@ -156,6 +167,8 @@ size_t GCDump::DumpInfoHdr (PTR_CBYTE gcInfoBlock, gcPrintf(" Sync region = [%u,%u] ([0x%x,0x%x])\n", header->syncStartOffset, header->syncEndOffset, header->syncStartOffset, header->syncEndOffset); + if (header->noGCRegionCnt > 0) + gcPrintf(" no GC region count = %2u \n", header->noGCRegionCnt); if (header->epilogCount > 1 || (header->epilogCount != 0 && header->epilogAtEnd == 0)) @@ -232,6 +245,23 @@ size_t GCDump::DumpGCTable(PTR_CBYTE table, if (header.ebxSaved) calleeSavedRegs++; } + /* Dump the no GC region table */ + + if (header.noGCRegionCnt > 0) + { + count = header.noGCRegionCnt; + while (count-- > 0) + { + unsigned regionOffset; + unsigned regionSize; + + table += decodeUnsigned(table, ®ionOffset); + table += decodeUnsigned(table, ®ionSize); + + gcPrintf("[%04X-%04X) no GC region\n", regionOffset, regionOffset + regionSize); + } + } + /* Dump the untracked frame variable table */ count = header.untrackedCnt; @@ -995,6 +1025,12 @@ void GCDump::DumpPtrsInFrame(PTR_CBYTE gcInfoBlock, header.revPInvokeOffset = offset; _ASSERTE(offset != INVALID_REV_PINVOKE_OFFSET); } + if (header.noGCRegionCnt == HAS_NOGCREGIONS) + { + unsigned count; + table += decodeUnsigned(table, &count); + header.noGCRegionCnt = count; + } prologSize = header.prologSize; epilogSize = header.epilogSize; diff --git a/src/coreclr/inc/gc_unwind_x86.h b/src/coreclr/inc/gc_unwind_x86.h index 311c032c9ccc9d..46d44afec4f6c1 100644 --- a/src/coreclr/inc/gc_unwind_x86.h +++ b/src/coreclr/inc/gc_unwind_x86.h @@ -367,6 +367,8 @@ struct hdrInfo unsigned int syncEpilogStart; // The start of the epilog. Synchronized methods are guaranteed to have no more than one epilog. unsigned int revPInvokeOffset; // INVALID_REV_PINVOKE_OFFSET if there is no Reverse PInvoke frame + unsigned int noGCRegionCnt; + enum { NOT_IN_PROLOG = -1, NOT_IN_EPILOG = -1 }; int prologOffs; // NOT_IN_PROLOG if not in prolog @@ -403,4 +405,8 @@ size_t DecodeGCHdrInfo(GCInfoToken gcInfoToken, unsigned curOffset, hdrInfo * infoPtr); +bool IsInNoGCRegion(hdrInfo * infoPtr, + PTR_CBYTE table, + unsigned curOffset); + #endif // _UNWIND_X86_H diff --git a/src/coreclr/inc/gcdecoder.cpp b/src/coreclr/inc/gcdecoder.cpp index d4a3c4c3a6f5de..26001f588944e5 100644 --- a/src/coreclr/inc/gcdecoder.cpp +++ b/src/coreclr/inc/gcdecoder.cpp @@ -205,9 +205,22 @@ PTR_CBYTE FASTCALL decodeHeader(PTR_CBYTE table, UINT32 version, InfoHdr* header nextByte = *table++; encoding = nextByte & ADJ_ENCODING_MAX; // encoding here always corresponds to codes in InfoHdrAdjust2 set - - _ASSERTE(encoding < SET_RET_KIND_MAX); - header->returnKind = (ReturnKind)encoding; + if (encoding <= SET_RET_KIND_MAX) + { + header->returnKind = (ReturnKind)encoding; + } + else if (encoding < FFFF_NOGCREGION_CNT) + { + header->noGCRegionCnt = encoding - SET_NOGCREGIONS_CNT; + } + else if (encoding == FFFF_NOGCREGION_CNT) + { + header->noGCRegionCnt = HAS_NOGCREGIONS; + } + else + { + _ASSERTE(!"Unexpected encoding"); + } break; } } @@ -470,7 +483,8 @@ bool InfoHdrSmall::isHeaderMatch(const InfoHdr& target) const target.varPtrTableSize != HAS_VARPTR && target.gsCookieOffset != HAS_GS_COOKIE_OFFSET && target.syncStartOffset != HAS_SYNC_OFFSET && - target.revPInvokeOffset != HAS_REV_PINVOKE_FRAME_OFFSET); + target.revPInvokeOffset != HAS_REV_PINVOKE_FRAME_OFFSET && + target.noGCRegionCnt != HAS_NOGCREGIONS); #endif // compare two InfoHdr's up to but not including the untrackCnt field @@ -495,7 +509,10 @@ bool InfoHdrSmall::isHeaderMatch(const InfoHdr& target) const if (target.syncStartOffset != INVALID_SYNC_OFFSET) return false; - if (target.revPInvokeOffset!= INVALID_REV_PINVOKE_OFFSET) + if (target.revPInvokeOffset != INVALID_REV_PINVOKE_OFFSET) + return false; + + if (target.noGCRegionCnt > 0) return false; return true; diff --git a/src/coreclr/inc/gcinfotypes.h b/src/coreclr/inc/gcinfotypes.h index 0218a49853a1cd..10d22d670947b9 100644 --- a/src/coreclr/inc/gcinfotypes.h +++ b/src/coreclr/inc/gcinfotypes.h @@ -320,7 +320,8 @@ enum infoHdrAdjustConstants { SET_EPILOGSIZE_MAX = 10, // Change to 6 SET_EPILOGCNT_MAX = 4, SET_UNTRACKED_MAX = 3, - SET_RET_KIND_MAX = 4, // 2 bits for ReturnKind + SET_RET_KIND_MAX = 3, // 2 bits for ReturnKind + SET_NOGCREGIONS_MAX = 4, ADJ_ENCODING_MAX = 0x7f, // Maximum valid encoding in a byte // Also used to mask off next bit from each encoding byte. MORE_BYTES_TO_FOLLOW = 0x80 // If the High-bit of a header or adjustment byte @@ -372,10 +373,13 @@ enum infoHdrAdjust { // Second set of opcodes, when first code is 0x4F enum infoHdrAdjust2 { SET_RETURNKIND = 0, // 0x00-SET_RET_KIND_MAX Set ReturnKind to value + SET_NOGCREGIONS_CNT = SET_RETURNKIND + SET_RET_KIND_MAX + 1, // 0x04 + FFFF_NOGCREGION_CNT = SET_NOGCREGIONS_CNT + SET_NOGCREGIONS_MAX + 1 // 0x09 There is a count (>SET_NOGCREGIONS_MAX) after the header encoding }; #define HAS_UNTRACKED ((unsigned int) -1) #define HAS_VARPTR ((unsigned int) -1) +#define HAS_NOGCREGIONS ((unsigned int) -1) // 0 is a valid offset for the Reverse P/Invoke block // So use -1 as the sentinel for invalid and -2 as the sentinel for present. @@ -424,7 +428,7 @@ struct InfoHdrSmall { unsigned short argCount; // 5,6 in bytes unsigned int frameSize; // 7,8,9,10 in bytes unsigned int untrackedCnt; // 11,12,13,14 - unsigned int varPtrTableSize; // 15.16,17,18 + unsigned int varPtrTableSize; // 15,16,17,18 // Checks whether "this" is compatible with "target". // It is not an exact bit match as "this" could have some @@ -442,7 +446,8 @@ struct InfoHdr : public InfoHdrSmall { unsigned int syncStartOffset; // 23,24,25,26 unsigned int syncEndOffset; // 27,28,29,30 unsigned int revPInvokeOffset; // 31,32,33,34 Available GcInfo v2 onwards, previously undefined - // 35 bytes total + unsigned int noGCRegionCnt; // 35,36,37,38 + // 39 bytes total // Checks whether "this" is compatible with "target". // It is not an exact bit match as "this" could have some @@ -457,7 +462,8 @@ struct InfoHdr : public InfoHdrSmall { target.varPtrTableSize != HAS_VARPTR && target.gsCookieOffset != HAS_GS_COOKIE_OFFSET && target.syncStartOffset != HAS_SYNC_OFFSET && - target.revPInvokeOffset != HAS_REV_PINVOKE_FRAME_OFFSET); + target.revPInvokeOffset != HAS_REV_PINVOKE_FRAME_OFFSET && + target.noGCRegionCnt != HAS_NOGCREGIONS); #endif // compare two InfoHdr's up to but not including the untrackCnt field @@ -488,6 +494,13 @@ struct InfoHdr : public InfoHdrSmall { (target.revPInvokeOffset == INVALID_REV_PINVOKE_OFFSET)) return false; + if (noGCRegionCnt != target.noGCRegionCnt) { + if (target.noGCRegionCnt <= SET_NOGCREGIONS_MAX) + return false; + else if (noGCRegionCnt != HAS_UNTRACKED) + return false; + } + return true; } }; @@ -518,6 +531,7 @@ inline void GetInfoHdr(int index, InfoHdr * header) header->syncStartOffset = INVALID_SYNC_OFFSET; header->syncEndOffset = INVALID_SYNC_OFFSET; header->revPInvokeOffset = INVALID_REV_PINVOKE_OFFSET; + header->noGCRegionCnt = 0; } PTR_CBYTE FASTCALL decodeHeader(PTR_CBYTE table, UINT32 version, InfoHdr* header); diff --git a/src/coreclr/jit/codegenxarch.cpp b/src/coreclr/jit/codegenxarch.cpp index 1df741994254ce..01f639dc7ddc14 100644 --- a/src/coreclr/jit/codegenxarch.cpp +++ b/src/coreclr/jit/codegenxarch.cpp @@ -202,14 +202,10 @@ BasicBlock* CodeGen::genCallFinally(BasicBlock* block) } else { -// TODO-Linux-x86: Do we need to handle the GC information for this NOP or JMP specially, as is done for other -// architectures? -#ifndef JIT32_GCENCODER // Because of the way the flowgraph is connected, the liveness info for this one instruction // after the call is not (can not be) correct in cases where a variable has a last use in the // handler. So turn off GC reporting once we execute the call and reenable after the jmp/nop GetEmitter()->emitDisableGC(); -#endif // JIT32_GCENCODER GetEmitter()->emitIns_J(INS_call, block->GetTarget()); @@ -229,9 +225,7 @@ BasicBlock* CodeGen::genCallFinally(BasicBlock* block) inst_JMP(EJ_jmp, finallyContinuation); } -#ifndef JIT32_GCENCODER GetEmitter()->emitEnableGC(); -#endif // JIT32_GCENCODER } } #if defined(FEATURE_EH_WINDOWS_X86) @@ -1879,11 +1873,9 @@ void CodeGen::genCodeForTreeNode(GenTree* treeNode) switch (treeNode->gtOper) { -#ifndef JIT32_GCENCODER case GT_START_NONGC: GetEmitter()->emitDisableGC(); break; -#endif // !defined(JIT32_GCENCODER) case GT_START_PREEMPTGC: // Kill callee saves GC registers, and create a label @@ -3160,9 +3152,7 @@ void CodeGen::genCodeForStoreBlk(GenTreeBlk* storeBlkNode) { case GenTreeBlk::BlkOpKindCpObjRepInstr: case GenTreeBlk::BlkOpKindCpObjUnroll: -#ifndef JIT32_GCENCODER assert(!storeBlkNode->gtBlkOpGcUnsafe); -#endif genCodeForCpObj(storeBlkNode->AsBlk()); break; @@ -3172,9 +3162,7 @@ void CodeGen::genCodeForStoreBlk(GenTreeBlk* storeBlkNode) break; case GenTreeBlk::BlkOpKindRepInstr: -#ifndef JIT32_GCENCODER assert(!storeBlkNode->gtBlkOpGcUnsafe); -#endif if (isCopyBlk) { genCodeForCpBlkRepMovs(storeBlkNode); @@ -3188,12 +3176,10 @@ void CodeGen::genCodeForStoreBlk(GenTreeBlk* storeBlkNode) case GenTreeBlk::BlkOpKindUnroll: if (isCopyBlk) { -#ifndef JIT32_GCENCODER if (storeBlkNode->gtBlkOpGcUnsafe) { GetEmitter()->emitDisableGC(); } -#endif if (storeBlkNode->gtBlkOpKind == GenTreeBlk::BlkOpKindUnroll) { genCodeForCpBlkUnroll(storeBlkNode); @@ -3203,18 +3189,14 @@ void CodeGen::genCodeForStoreBlk(GenTreeBlk* storeBlkNode) assert(storeBlkNode->gtBlkOpKind == GenTreeBlk::BlkOpKindUnrollMemmove); genCodeForMemmove(storeBlkNode); } -#ifndef JIT32_GCENCODER if (storeBlkNode->gtBlkOpGcUnsafe) { GetEmitter()->emitEnableGC(); } -#endif } else { -#ifndef JIT32_GCENCODER assert(!storeBlkNode->gtBlkOpGcUnsafe); -#endif genCodeForInitBlkUnroll(storeBlkNode); } break; diff --git a/src/coreclr/jit/emit.cpp b/src/coreclr/jit/emit.cpp index 40264818097361..9e7c3cefeaa5af 100644 --- a/src/coreclr/jit/emit.cpp +++ b/src/coreclr/jit/emit.cpp @@ -10597,7 +10597,6 @@ regMaskTP emitter::emitGetGCRegsKilledByNoGCCall(CorInfoHelpFunc helper) return result; } -#if !defined(JIT32_GCENCODER) //------------------------------------------------------------------------ // emitDisableGC: Requests that the following instruction groups are not GC-interruptible. // @@ -10689,4 +10688,3 @@ void emitter::emitEnableGC() JITDUMP("Enable GC: still %u no-gc requests\n", emitNoGCRequestCount); } } -#endif // !defined(JIT32_GCENCODER) diff --git a/src/coreclr/jit/emit.h b/src/coreclr/jit/emit.h index f9c37456e24174..0f11db154ed27c 100644 --- a/src/coreclr/jit/emit.h +++ b/src/coreclr/jit/emit.h @@ -2891,11 +2891,9 @@ class emitter void emitNewIG(); -#if !defined(JIT32_GCENCODER) void emitDisableGC(); void emitEnableGC(); bool emitGCDisabled(); -#endif // !defined(JIT32_GCENCODER) #if defined(TARGET_XARCH) static bool emitAlignInstHasNoCode(instrDesc* id); diff --git a/src/coreclr/jit/emitinl.h b/src/coreclr/jit/emitinl.h index a586193dd5b714..02aab9d4ed910e 100644 --- a/src/coreclr/jit/emitinl.h +++ b/src/coreclr/jit/emitinl.h @@ -585,10 +585,17 @@ inline bool insIsCMOV(instruction ins) * false. Returns the final result of the callback. */ template -bool emitter::emitGenNoGCLst(Callback& cb) +bool emitter::emitGenNoGCLst(Callback& cb, bool skipAllPrologsAndEpilogs /* = false */) { for (insGroup* ig = emitIGlist; ig; ig = ig->igNext) { + if (skipAllPrologsAndEpilogs) + { + if (ig == emitPrologIG) + continue; + if (ig->igFlags & (IGF_EPILOG | IGF_FUNCLET_PROLOG | IGF_FUNCLET_EPILOG)) + continue; + } if ((ig->igFlags & IGF_NOGCINTERRUPT) && ig->igSize > 0) { emitter::instrDesc* id = emitFirstInstrDesc(ig->igData); diff --git a/src/coreclr/jit/emitpub.h b/src/coreclr/jit/emitpub.h index bf15ba33667cac..5725c04e6f6352 100644 --- a/src/coreclr/jit/emitpub.h +++ b/src/coreclr/jit/emitpub.h @@ -43,7 +43,7 @@ unsigned emitEndCodeGen(Compiler* comp, unsigned emitGetEpilogCnt(); template -bool emitGenNoGCLst(Callback& cb); +bool emitGenNoGCLst(Callback& cb, bool skipAllPrologsAndEpilogs = false); void emitBegProlog(); unsigned emitGetPrologOffsetEstimate(); diff --git a/src/coreclr/jit/gcencode.cpp b/src/coreclr/jit/gcencode.cpp index a8ba3bf4b23ad0..b473b6ff0dbfd3 100644 --- a/src/coreclr/jit/gcencode.cpp +++ b/src/coreclr/jit/gcencode.cpp @@ -886,7 +886,7 @@ BYTE FASTCALL encodeHeaderNext(const InfoHdr& header, InfoHdr* state, BYTE& code state->returnKind = header.returnKind; codeSet = 2; // Two byte encoding encoding = header.returnKind; - _ASSERTE(encoding < SET_RET_KIND_MAX); + _ASSERTE(encoding <= SET_RET_KIND_MAX); goto DO_RETURN; } @@ -950,6 +950,27 @@ BYTE FASTCALL encodeHeaderNext(const InfoHdr& header, InfoHdr* state, BYTE& code } } + if (state->noGCRegionCnt != header.noGCRegionCnt) + { + assert(state->noGCRegionCnt <= SET_NOGCREGIONS_MAX || state->noGCRegionCnt == HAS_NOGCREGIONS); + + // We have two-byte encodings for 0..4 + if (header.noGCRegionCnt <= SET_NOGCREGIONS_MAX) + { + state->noGCRegionCnt = header.noGCRegionCnt; + codeSet = 2; + encoding = (BYTE)(SET_NOGCREGIONS_CNT + header.noGCRegionCnt); + goto DO_RETURN; + } + else if (state->noGCRegionCnt != HAS_NOGCREGIONS) + { + state->noGCRegionCnt = HAS_NOGCREGIONS; + codeSet = 2; + encoding = FFFF_NOGCREGION_CNT; + goto DO_RETURN; + } + } + DO_RETURN: _ASSERTE(encoding < MORE_BYTES_TO_FOLLOW); if (!state->isHeaderMatch(header)) @@ -964,7 +985,7 @@ static int measureDistance(const InfoHdr& header, const InfoHdrSmall* p, int clo if (p->untrackedCnt != header.untrackedCnt) { - if (header.untrackedCnt > 3) + if (header.untrackedCnt > SET_UNTRACKED_MAX) { if (p->untrackedCnt != HAS_UNTRACKED) distance += 1; @@ -1199,6 +1220,13 @@ static int measureDistance(const InfoHdr& header, const InfoHdrSmall* p, int clo return distance; } + if (header.noGCRegionCnt > 0) + { + distance += 2; + if (distance >= closeness) + return distance; + } + return distance; } @@ -1546,7 +1574,7 @@ size_t GCInfo::gcInfoBlockHdrSave( ReturnKind returnKind = getReturnKind(); _ASSERTE(IsValidReturnKind(returnKind) && "Return Kind must be valid"); _ASSERTE(!IsStructReturnKind(returnKind) && "Struct Return Kinds Unexpected for JIT32"); - _ASSERTE(((int)returnKind < (int)SET_RET_KIND_MAX) && "ReturnKind has no legal encoding"); + _ASSERTE(((int)returnKind <= (int)SET_RET_KIND_MAX) && "ReturnKind has no legal encoding"); header->returnKind = returnKind; header->gsCookieOffset = INVALID_GS_COOKIE_OFFSET; @@ -1599,7 +1627,8 @@ size_t GCInfo::gcInfoBlockHdrSave( if (mask == 0) { gcCountForHeader((UNALIGNED unsigned int*)&header->untrackedCnt, - (UNALIGNED unsigned int*)&header->varPtrTableSize); + (UNALIGNED unsigned int*)&header->varPtrTableSize, + (UNALIGNED unsigned int*)&header->noGCRegionCnt); } // @@ -1696,6 +1725,14 @@ size_t GCInfo::gcInfoBlockHdrSave( dest += (sz & mask); } + if (header->noGCRegionCnt > SET_NOGCREGIONS_MAX) + { + unsigned count = header->noGCRegionCnt; + unsigned sz = encodeUnsigned(mask ? dest : NULL, count); + size += sz; + dest += (sz & mask); + } + if (header->epilogCount) { /* Generate table unless one epilog at the end of the method */ @@ -2088,6 +2125,29 @@ unsigned PendingArgsStack::pasEnumGCoffs(unsigned iter, unsigned* offs) return pasENUM_END; } +// Small helper class to handle the No-GC-Interrupt callbacks +// when reporting interruptible ranges. +class NoGCRegionEncoder +{ + BYTE* dest; +public: + size_t totalSize; + + NoGCRegionEncoder(BYTE* dest) + : dest(dest) + , totalSize(0) + { + } + + // This callback is called for each insGroup marked with IGF_NOGCINTERRUPT. + bool operator()(unsigned igFuncIdx, unsigned igOffs, unsigned igSize, unsigned firstInstrSize, bool isInProlog) + { + totalSize += encodeUnsigned(dest == NULL ? NULL : dest + totalSize, igOffs); + totalSize += encodeUnsigned(dest == NULL ? NULL : dest + totalSize, igSize); + return true; + } +}; + /***************************************************************************** * * Generate the register pointer map, and return its total size in bytes. If @@ -2109,7 +2169,8 @@ size_t GCInfo::gcMakeRegPtrTable(BYTE* dest, int mask, const InfoHdr& header, un /* Start computing the total size of the table */ - bool emitArgTabOffset = (header.varPtrTableSize != 0 || header.untrackedCnt > SET_UNTRACKED_MAX); + bool emitArgTabOffset = + (header.varPtrTableSize != 0 || header.untrackedCnt > SET_UNTRACKED_MAX || header.noGCRegionCnt != 0); if (mask != 0 && emitArgTabOffset) { assert(*pArgTabOffset <= MAX_UNSIGNED_SIZE_T); @@ -2129,18 +2190,29 @@ size_t GCInfo::gcMakeRegPtrTable(BYTE* dest, int mask, const InfoHdr& header, un /************************************************************************** * - * Untracked ptr variables + * Untracked ptr variables and no GC regions * ************************************************************************** */ #if DEBUG unsigned untrackedCount = 0; unsigned varPtrTableSize = 0; - gcCountForHeader(&untrackedCount, &varPtrTableSize); + unsigned noGCRegionCount = 0; + gcCountForHeader(&untrackedCount, &varPtrTableSize, &noGCRegionCount); assert(untrackedCount == header.untrackedCnt); assert(varPtrTableSize == header.varPtrTableSize); + assert(noGCRegionCount == header.noGCRegionCnt); #endif // DEBUG + if (header.noGCRegionCnt != 0) + { + NoGCRegionEncoder encoder(mask != 0 ? dest : NULL); + compiler->GetEmitter()->emitGenNoGCLst(encoder, /* skipAllPrologsAndEpilogs = */ true); + totalSize += encoder.totalSize; + if (mask != 0) + dest += encoder.totalSize; + } + if (header.untrackedCnt != 0) { // Write the table of untracked pointer variables. @@ -3582,7 +3654,14 @@ size_t GCInfo::gcInfoBlockHdrDump(const BYTE* table, InfoHdr* header, unsigned* size_t GCInfo::gcDumpPtrTable(const BYTE* table, const InfoHdr& header, unsigned methodSize) { - printf("Pointer table:\n"); + if (header.noGCRegionCnt > 0) + { + printf("No GC regions and pointer table:\n"); + } + else + { + printf("Pointer table:\n"); + } GCDump gcDump(GCINFO_VERSION); diff --git a/src/coreclr/jit/gcinfo.cpp b/src/coreclr/jit/gcinfo.cpp index ff534a0afcbf21..3f6f5169d3950a 100644 --- a/src/coreclr/jit/gcinfo.cpp +++ b/src/coreclr/jit/gcinfo.cpp @@ -419,12 +419,33 @@ GCInfo::regPtrDsc* GCInfo::gcRegPtrAllocDsc() #ifdef JIT32_GCENCODER +// Small helper class to handle the No-GC-Interrupt callbacks +// when reporting interruptible ranges. +struct NoGCRegionCounter +{ + unsigned noGCRegionCount; + + NoGCRegionCounter() + : noGCRegionCount(0) + { + } + + // This callback is called for each insGroup marked with IGF_NOGCINTERRUPT. + bool operator()(unsigned igFuncIdx, unsigned igOffs, unsigned igSize, unsigned firstInstrSize, bool isInProlog) + { + noGCRegionCount++; + return true; + } +}; + /***************************************************************************** * * Compute the various counts that get stored in the info block header. */ -void GCInfo::gcCountForHeader(UNALIGNED unsigned int* pUntrackedCount, UNALIGNED unsigned int* pVarPtrTableSize) +void GCInfo::gcCountForHeader(UNALIGNED unsigned int* pUntrackedCount, + UNALIGNED unsigned int* pVarPtrTableSize, + UNALIGNED unsigned int* pNoGCRegionCount) { unsigned varNum; LclVarDsc* varDsc; @@ -556,6 +577,19 @@ void GCInfo::gcCountForHeader(UNALIGNED unsigned int* pUntrackedCount, UNALIGNED #endif *pVarPtrTableSize = varPtrTableSize; + + // Count the number of no GC regions + + unsigned int noGCRegionCount = 0; + + if (compiler->codeGen->GetInterruptible()) + { + NoGCRegionCounter counter; + compiler->GetEmitter()->emitGenNoGCLst(counter, /* skipAllPrologsAndEpilogs = */ true); + noGCRegionCount = counter.noGCRegionCount; + } + + *pNoGCRegionCount = noGCRegionCount; } //------------------------------------------------------------------------ diff --git a/src/coreclr/jit/gentree.h b/src/coreclr/jit/gentree.h index e57d334a2805b5..5f2e8d1eb2bfea 100644 --- a/src/coreclr/jit/gentree.h +++ b/src/coreclr/jit/gentree.h @@ -7900,9 +7900,7 @@ struct GenTreeBlk : public GenTreeIndir BlkOpKindUnrollMemmove, } gtBlkOpKind; -#ifndef JIT32_GCENCODER bool gtBlkOpGcUnsafe; -#endif bool ContainsReferences() { @@ -7940,11 +7938,9 @@ struct GenTreeBlk : public GenTreeIndir assert(layout != nullptr); assert(layout->GetSize() != 0); - m_layout = layout; - gtBlkOpKind = BlkOpKindInvalid; -#ifndef JIT32_GCENCODER + m_layout = layout; + gtBlkOpKind = BlkOpKindInvalid; gtBlkOpGcUnsafe = false; -#endif } #if DEBUGGABLE_GENTREE diff --git a/src/coreclr/jit/jitgcinfo.h b/src/coreclr/jit/jitgcinfo.h index 6d04e3b22734b3..ae9e4852a5d87c 100644 --- a/src/coreclr/jit/jitgcinfo.h +++ b/src/coreclr/jit/jitgcinfo.h @@ -289,7 +289,9 @@ class GCInfo //------------------------------------------------------------------------- #ifdef JIT32_GCENCODER - void gcCountForHeader(UNALIGNED unsigned int* pUntrackedCount, UNALIGNED unsigned int* pVarPtrTableSize); + void gcCountForHeader(UNALIGNED unsigned int* pUntrackedCount, + UNALIGNED unsigned int* pVarPtrTableSize, + UNALIGNED unsigned int* pNoGCRegionCount); bool gcIsUntrackedLocalOrNonEnregisteredArg(unsigned varNum, bool* pThisKeptAliveIsInUntracked = nullptr); diff --git a/src/coreclr/nativeaot/Runtime/windows/CoffNativeCodeManager.cpp b/src/coreclr/nativeaot/Runtime/windows/CoffNativeCodeManager.cpp index a2debab5499ee6..26df766b18b4a8 100644 --- a/src/coreclr/nativeaot/Runtime/windows/CoffNativeCodeManager.cpp +++ b/src/coreclr/nativeaot/Runtime/windows/CoffNativeCodeManager.cpp @@ -425,9 +425,16 @@ bool CoffNativeCodeManager::IsSafePoint(PTR_VOID pvAddress) #else // Extract the necessary information from the info block header hdrInfo info; - DecodeGCHdrInfo(GCInfoToken(gcInfo), codeOffset, &info); + size_t infoSize = DecodeGCHdrInfo(GCInfoToken(gcInfo), codeOffset, &info); + PTR_CBYTE table = gcInfo + infoSize; + + if (info.prologOffs != hdrInfo::NOT_IN_PROLOG || info.epilogOffs != hdrInfo::NOT_IN_EPILOG) + return false; + + if (!info.interruptible) + return false; - return info.interruptible && info.prologOffs == hdrInfo::NOT_IN_PROLOG && info.epilogOffs == hdrInfo::NOT_IN_EPILOG; + return !IsInNoGCRegion(&info, table, codeOffset); #endif } diff --git a/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/GCInfoTypes.cs b/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/GCInfoTypes.cs index 653425781a7cf6..83ac2ef374767d 100644 --- a/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/GCInfoTypes.cs +++ b/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/GCInfoTypes.cs @@ -19,7 +19,8 @@ enum InfoHdrAdjustConstants SET_EPILOGSIZE_MAX = 10, SET_EPILOGCNT_MAX = 4, SET_UNTRACKED_MAX = 3, - SET_RET_KIND_MAX = 4, + SET_RET_KIND_MAX = 3, + SET_NOGCREGIONS_MAX = 4, ADJ_ENCODING_MAX = 0x7f, MORE_BYTES_TO_FOLLOW = 0x80 }; @@ -67,6 +68,16 @@ enum InfoHdrAdjust NEXT_THREE_EPILOGSIZE = 0x78 }; + /// + /// Second set of opcodes, when first code is 0x4F + /// + enum InfoHdrAdjust2 + { + SET_RETURNKIND = 0, // 0x00-SET_RET_KIND_MAX Set ReturnKind to value + SET_NOGCREGIONS_CNT = SET_RETURNKIND + InfoHdrAdjustConstants.SET_RET_KIND_MAX + 1, // 0x04 + FFFF_NOGCREGION_CNT = SET_NOGCREGIONS_CNT + InfoHdrAdjustConstants.SET_NOGCREGIONS_MAX + 1 // 0x09 There is a count (>SET_NOGCREGIONS_MAX) after the header encoding + }; + /// /// based on macros defined in src\inc\gcinfotypes.h /// diff --git a/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/x86/GcInfo.cs b/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/x86/GcInfo.cs index ddb44a1d312ad1..7b6f466c2f1a3b 100644 --- a/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/x86/GcInfo.cs +++ b/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/x86/GcInfo.cs @@ -13,6 +13,7 @@ public class GcInfo : BaseGcInfo const uint byref_OFFSET_FLAG = 0x1; public InfoHdrSmall Header { get; set; } + public NoGcRegionTable NoGCRegions { get; set; } public GcSlotTable SlotTable { get; set; } public GcInfo() { } @@ -28,6 +29,8 @@ public GcInfo(byte[] image, int offset) Header = InfoHdrDecoder.DecodeHeader(image, ref offset, CodeLength); + NoGCRegions = new NoGcRegionTable(image, Header, ref offset); + SlotTable = new GcSlotTable(image, Header, ref offset); Transitions = new Dictionary>(); @@ -54,6 +57,7 @@ public override string ToString() sb.AppendLine($" CodeLength: {CodeLength} bytes"); sb.AppendLine($" InfoHdr:"); sb.AppendLine($"{Header}"); + sb.AppendLine($"{NoGCRegions}"); sb.AppendLine($"{SlotTable}"); sb.AppendLine($" Size: {Size} bytes"); diff --git a/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/x86/InfoHdr.cs b/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/x86/InfoHdr.cs index 14148a9ddc27a7..7852ebf962db60 100644 --- a/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/x86/InfoHdr.cs +++ b/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/x86/InfoHdr.cs @@ -44,6 +44,7 @@ public struct InfoHdrSmall public uint SyncStartOffset { get; set; } public uint SyncEndOffset { get; set; } public uint RevPInvokeOffset { get; set; } + public uint NoGCRegionCnt { get; set; } public bool HasArgTabOffset { get; set; } public uint ArgTabOffset { get; set; } @@ -82,6 +83,7 @@ public InfoHdrSmall(uint prologSize, uint epilogSize, byte epilogCount, byte epi SyncStartOffset = 0; SyncEndOffset = 0; RevPInvokeOffset = 0; + NoGCRegionCnt = 0; HasArgTabOffset = false; ArgTabOffset = 0; @@ -138,6 +140,10 @@ public override string ToString() { sb.AppendLine($" Sync region = [{SyncStartOffset},{SyncEndOffset}]"); } + if (NoGCRegionCnt > 0) + { + sb.AppendLine($" No GC region count = {NoGCRegionCnt}"); + } sb.Append($" Epilogs:"); foreach (int epilog in Epilogs) @@ -158,6 +164,7 @@ public class InfoHdrDecoder { private const uint HAS_GS_COOKIE_OFFSET = 0xFFFFFFFF; private const uint HAS_SYNC_OFFSET = 0xFFFFFFFF; private const uint HAS_REV_PINVOKE_FRAME_OFFSET = 0xFFFFFFFF; + private const uint HAS_NOGCREGIONS = 0xFFFFFFFF; private const uint YES = HAS_VARPTR; /// @@ -278,11 +285,18 @@ public static InfoHdrSmall DecodeHeader(byte[] image, ref int offset, int codeLe nextByte = image[offset++]; encoding = (byte)(nextByte & (int)InfoHdrAdjustConstants.ADJ_ENCODING_MAX); // encoding here always corresponds to codes in InfoHdrAdjust2 set - - if (encoding < (int)InfoHdrAdjustConstants.SET_RET_KIND_MAX) + if (encoding <= (int)InfoHdrAdjustConstants.SET_RET_KIND_MAX) { header.ReturnKind = (ReturnKinds)encoding; } + else if (encoding < (int)InfoHdrAdjust2.FFFF_NOGCREGION_CNT) + { + header.NoGCRegionCnt = (uint)encoding - (uint)InfoHdrAdjust2.SET_NOGCREGIONS_CNT; + } + else if (encoding == (int)InfoHdrAdjust2.FFFF_NOGCREGION_CNT) + { + header.NoGCRegionCnt = HAS_NOGCREGIONS; + } else { throw new BadImageFormatException("Unexpected gcinfo header encoding"); @@ -351,6 +365,10 @@ public static InfoHdrSmall DecodeHeader(byte[] image, ref int offset, int codeLe { header.RevPInvokeOffset = NativeReader.DecodeUnsignedGc(image, ref offset); } + if (header.NoGCRegionCnt == HAS_NOGCREGIONS) + { + header.NoGCRegionCnt = NativeReader.DecodeUnsignedGc(image, ref offset); + } header.Epilogs = new List(); if (header.EpilogCount > 1 || (header.EpilogCount != 0 && !header.EpilogAtEnd)) diff --git a/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/x86/NoGcRegionTable.cs b/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/x86/NoGcRegionTable.cs new file mode 100644 index 00000000000000..4dcc332cb7ce73 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/x86/NoGcRegionTable.cs @@ -0,0 +1,65 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Generic; +using System.Reflection.PortableExecutable; +using System.Text; + +namespace ILCompiler.Reflection.ReadyToRun.x86 +{ + public class NoGcRegionTable + { + public class NoGcRegion + { + public uint Offset { get; set; } + public uint Size { get; set; } + + public NoGcRegion(uint offset, uint size) + { + Offset = offset; + Size = size; + } + + public override string ToString() + { + return $" [{Offset:04X}-{Offset+Size:04X})\n"; + } + } + + public List Regions { get; set; } + + public NoGcRegionTable() { } + + public NoGcRegionTable(byte[] image, InfoHdrSmall header, ref int offset) + { + Regions = new List((int)header.NoGCRegionCnt); + + uint count = header.NoGCRegionCnt; + while (count-- > 0) + { + uint regionOffset = NativeReader.DecodeUnsignedGc(image, ref offset); + uint regionSize = NativeReader.DecodeUnsignedGc(image, ref offset); + Regions.Add(new NoGcRegion(regionOffset, regionSize)); + } + } + + public override string ToString() + { + if (Regions.Count > 0) + { + StringBuilder sb = new StringBuilder(); + + sb.AppendLine($" No GC regions:"); + foreach (NoGcRegion region in Regions) + { + sb.Append(region.ToString()); + } + + return sb.ToString(); + } + + return string.Empty; + } + } +} diff --git a/src/coreclr/vm/eetwain.cpp b/src/coreclr/vm/eetwain.cpp index 9290e3f15a9d93..7db5fdc7f2733e 100644 --- a/src/coreclr/vm/eetwain.cpp +++ b/src/coreclr/vm/eetwain.cpp @@ -851,18 +851,21 @@ bool EECodeManager::IsGcSafe( EECodeInfo *pCodeInfo, SUPPORTS_DAC; } CONTRACTL_END; - hdrInfo info = { 0 }; + hdrInfo info = { 0 }; /* Extract the necessary information from the info block header */ - pCodeInfo->DecodeGCHdrInfo(&info, dwRelOffset); + PTR_CBYTE table = pCodeInfo->DecodeGCHdrInfo(&info, dwRelOffset); /* workaround: prevent interruption within prolog/epilog */ if (info.prologOffs != hdrInfo::NOT_IN_PROLOG || info.epilogOffs != hdrInfo::NOT_IN_EPILOG) return false; - return (info.interruptible); + if (!info.interruptible) + return false; + + return !::IsInNoGCRegion(&info, table, dwRelOffset); } #endif // !USE_GC_INFO_DECODER @@ -1494,13 +1497,20 @@ OBJECTREF EECodeManager::GetInstance( PREGDISPLAY pContext, _ASSERTE(*castto(table, unsigned short *)++ == 0xBEEF); #endif + /* Skip over no-GC region table */ + unsigned count = hdrInfoBody->noGCRegionCnt; + while (count-- > 0) + { + fastSkipUnsigned(table); fastSkipUnsigned(table); + } + #ifndef FEATURE_EH_FUNCLETS /* Parse the untracked frame variable table */ /* The 'this' pointer can never be located in the untracked table */ /* as we only allow pinned and byrefs in the untracked table */ - unsigned count = hdrInfoBody->untrackedCnt; + count = hdrInfoBody->untrackedCnt; while (count-- > 0) { fastSkipSigned(table); diff --git a/src/coreclr/vm/gc_unwind_x86.inl b/src/coreclr/vm/gc_unwind_x86.inl index e1502ec6049e9a..c5d45a6c43074c 100644 --- a/src/coreclr/vm/gc_unwind_x86.inl +++ b/src/coreclr/vm/gc_unwind_x86.inl @@ -197,6 +197,17 @@ size_t DecodeGCHdrInfo(GCInfoToken gcInfoToken, header.revPInvokeOffset = fastDecodeUnsigned(table); } + if (header.noGCRegionCnt == HAS_NOGCREGIONS) + { + hasArgTabOffset = TRUE; + header.noGCRegionCnt = fastDecodeUnsigned(table); + } + else if (header.noGCRegionCnt > 0) + { + hasArgTabOffset = TRUE; + } + + /* Some sanity checks on header */ _ASSERTE( header.prologSize + @@ -231,6 +242,7 @@ size_t DecodeGCHdrInfo(GCInfoToken gcInfoToken, infoPtr->syncStartOffset = header.syncStartOffset; infoPtr->syncEndOffset = header.syncEndOffset; infoPtr->revPInvokeOffset = header.revPInvokeOffset; + infoPtr->noGCRegionCnt = header.noGCRegionCnt; infoPtr->doubleAlign = header.doubleAlign; infoPtr->handlers = header.handlers; @@ -667,6 +679,37 @@ inline size_t GetSizeOfFrameHeaderForEnC(hdrInfo * info) } #endif // FEATURE_NATIVEAOT +/*****************************************************************************/ +bool IsInNoGCRegion(hdrInfo * infoPtr, + PTR_CBYTE table, + unsigned curOffset) +{ + CONTRACTL { + NOTHROW; + GC_NOTRIGGER; + SUPPORTS_DAC; + } CONTRACTL_END; + + if (infoPtr->noGCRegionCnt == 0) + return false; + +#if VERIFY_GC_TABLES + _ASSERTE(*castto(table, unsigned short *)++ == 0xBEEF); +#endif + + unsigned count = infoPtr->noGCRegionCnt; + while (count-- > 0) { + unsigned regionOffset = fastDecodeUnsigned(table); + if (curOffset < regionOffset) + return false; + unsigned regionSize = fastDecodeUnsigned(table); + if (curOffset - regionOffset < regionSize) + return true; + } + + return false; +} + /*****************************************************************************/ static PTR_CBYTE skipToArgReg(const hdrInfo& info, PTR_CBYTE table) @@ -692,6 +735,13 @@ PTR_CBYTE skipToArgReg(const hdrInfo& info, PTR_CBYTE table) _ASSERTE(*castto(table, unsigned short *)++ == 0xBEEF); #endif + /* Skip over the no GC regions table */ + + count = info.noGCRegionCnt; + while (count-- > 0) { + fastSkipUnsigned(table); fastSkipUnsigned(table); + } + /* Skip over the untracked frame variable table */ count = info.untrackedCnt; @@ -3583,6 +3633,12 @@ bool EnumGcRefsX86(PREGDISPLAY pContext, unsigned ptrAddr; unsigned lowBits; + /* Skip over no-GC region table */ + count = info.noGCRegionCnt; + while (count-- > 0) + { + fastSkipUnsigned(table); fastSkipUnsigned(table); + } /* Process the untracked frame variable table */