From 99bd8a8a3fa5cdec1ddca364cb8c503ff94fd828 Mon Sep 17 00:00:00 2001 From: vsadov <8218165+VSadov@users.noreply.github.com> Date: Sat, 21 Sep 2024 15:25:49 -0700 Subject: [PATCH 01/16] ReadyToRunVersionToGcInfoVersion --- src/coreclr/inc/gcinfo.h | 8 -------- 1 file changed, 8 deletions(-) diff --git a/src/coreclr/inc/gcinfo.h b/src/coreclr/inc/gcinfo.h index d526405c9f2cff..a400ecfbf5c9e8 100644 --- a/src/coreclr/inc/gcinfo.h +++ b/src/coreclr/inc/gcinfo.h @@ -67,14 +67,6 @@ struct GCInfoToken static uint32_t ReadyToRunVersionToGcInfoVersion(uint32_t readyToRunMajorVersion, uint32_t readyToRunMinorVersion) { - // Once MINIMUM_READYTORUN_MAJOR_VERSION is bumped to 10+ - // delete the following and just return GCINFO_VERSION - // - // R2R 9.0 and 9.1 use GCInfo v2 - // R2R 9.2 uses GCInfo v3 - if (readyToRunMajorVersion == 9 && readyToRunMinorVersion < 2) - return 2; - return GCINFO_VERSION; } }; From 890a89bd5253e7444f8f0a32ccccae71c64addee Mon Sep 17 00:00:00 2001 From: vsadov <8218165+VSadov@users.noreply.github.com> Date: Sat, 21 Sep 2024 15:56:40 -0700 Subject: [PATCH 02/16] removed AreSafePointsInterruptible, IsInterruptibleSafePoint, etc.. --- src/coreclr/inc/clrconfigvalues.h | 1 - src/coreclr/inc/eetwain.h | 3 --- src/coreclr/inc/gcinfodecoder.h | 4 +--- .../Runtime/unix/UnixNativeCodeManager.cpp | 4 ++-- .../Runtime/windows/CoffNativeCodeManager.cpp | 4 ++-- src/coreclr/vm/eetwain.cpp | 16 +++------------- src/coreclr/vm/gccover.cpp | 8 +++----- src/coreclr/vm/gcinfodecoder.cpp | 14 ++------------ 8 files changed, 13 insertions(+), 41 deletions(-) diff --git a/src/coreclr/inc/clrconfigvalues.h b/src/coreclr/inc/clrconfigvalues.h index 00d9745e177e7f..3097ff9f9c8914 100644 --- a/src/coreclr/inc/clrconfigvalues.h +++ b/src/coreclr/inc/clrconfigvalues.h @@ -507,7 +507,6 @@ CONFIG_DWORD_INFO(INTERNAL_DiagnosticSuspend, W("DiagnosticSuspend"), 0, "") CONFIG_DWORD_INFO(INTERNAL_SuspendDeadlockTimeout, W("SuspendDeadlockTimeout"), 40000, "") CONFIG_DWORD_INFO(INTERNAL_SuspendThreadDeadlockTimeoutMs, W("SuspendThreadDeadlockTimeoutMs"), 2000, "") RETAIL_CONFIG_DWORD_INFO(INTERNAL_ThreadSuspendInjection, W("INTERNAL_ThreadSuspendInjection"), 1, "Specifies whether to inject activations for thread suspension on Unix") -RETAIL_CONFIG_DWORD_INFO(INTERNAL_InterruptibleCallSites, W("InterruptibleCallSites"), 1, "Specifies whether to allow asynchronous thread interruptions at call sites (requires GCInfo v3)") /// /// Thread (miscellaneous) diff --git a/src/coreclr/inc/eetwain.h b/src/coreclr/inc/eetwain.h index c7b1be02e5c638..bee2f658ee7c08 100644 --- a/src/coreclr/inc/eetwain.h +++ b/src/coreclr/inc/eetwain.h @@ -457,9 +457,6 @@ virtual bool IsGcSafe( EECodeInfo *pCodeInfo, DWORD dwRelOffset); -static -bool InterruptibleSafePointsEnabled(); - #if defined(TARGET_ARM) || defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64) virtual bool HasTailCalls(EECodeInfo *pCodeInfo); diff --git a/src/coreclr/inc/gcinfodecoder.h b/src/coreclr/inc/gcinfodecoder.h index 6f62a3f8741998..a72d6cd0df4aa2 100644 --- a/src/coreclr/inc/gcinfodecoder.h +++ b/src/coreclr/inc/gcinfodecoder.h @@ -528,9 +528,7 @@ class GcInfoDecoder #ifdef PARTIALLY_INTERRUPTIBLE_GC_SUPPORTED bool IsSafePoint(); - bool AreSafePointsInterruptible(); - bool IsInterruptibleSafePoint(); - bool CouldBeInterruptibleSafePoint(); + bool CouldBeSafePoint(); // This is used for gccoverage bool IsSafePoint(UINT32 codeOffset); diff --git a/src/coreclr/nativeaot/Runtime/unix/UnixNativeCodeManager.cpp b/src/coreclr/nativeaot/Runtime/unix/UnixNativeCodeManager.cpp index b12d63bf726129..9f982f630bcec7 100644 --- a/src/coreclr/nativeaot/Runtime/unix/UnixNativeCodeManager.cpp +++ b/src/coreclr/nativeaot/Runtime/unix/UnixNativeCodeManager.cpp @@ -196,7 +196,7 @@ bool UnixNativeCodeManager::IsSafePoint(PTR_VOID pvAddress) if (decoder.IsInterruptible()) return true; - if (decoder.IsInterruptibleSafePoint()) + if (decoder.IsSafePoint()) return true; return false; @@ -246,7 +246,7 @@ void UnixNativeCodeManager::EnumGcRefs(MethodInfo * pMethodInfo, codeOffset - 1 ); - assert(decoder.IsInterruptibleSafePoint()); + assert(decoder.IsSafePoint()); } } diff --git a/src/coreclr/nativeaot/Runtime/windows/CoffNativeCodeManager.cpp b/src/coreclr/nativeaot/Runtime/windows/CoffNativeCodeManager.cpp index b8c2310d644004..66c775e0e2c05e 100644 --- a/src/coreclr/nativeaot/Runtime/windows/CoffNativeCodeManager.cpp +++ b/src/coreclr/nativeaot/Runtime/windows/CoffNativeCodeManager.cpp @@ -418,7 +418,7 @@ bool CoffNativeCodeManager::IsSafePoint(PTR_VOID pvAddress) if (decoder.IsInterruptible()) return true; - if (decoder.IsInterruptibleSafePoint()) + if (decoder.IsSafePoint()) return true; return false; @@ -479,7 +479,7 @@ void CoffNativeCodeManager::EnumGcRefs(MethodInfo * pMethodInfo, codeOffset - 1 ); - assert(decoder.IsInterruptibleSafePoint()); + assert(decoder.IsSafePoint()); } } diff --git a/src/coreclr/vm/eetwain.cpp b/src/coreclr/vm/eetwain.cpp index 5746c44de4a770..2176c9f28b8285 100644 --- a/src/coreclr/vm/eetwain.cpp +++ b/src/coreclr/vm/eetwain.cpp @@ -846,21 +846,12 @@ bool EECodeManager::IsGcSafe( EECodeInfo *pCodeInfo, if (gcInfoDecoder.IsInterruptible()) return true; - if (InterruptibleSafePointsEnabled() && gcInfoDecoder.IsInterruptibleSafePoint()) + if (gcInfoDecoder.IsSafePoint()) return true; return false; } -bool EECodeManager::InterruptibleSafePointsEnabled() -{ - LIMITED_METHOD_CONTRACT; - - // zero initialized - static ConfigDWORD interruptibleCallSitesEnabled; - return interruptibleCallSitesEnabled.val(CLRConfig::INTERNAL_InterruptibleCallSites) != 0; -} - #if defined(TARGET_ARM) || defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64) bool EECodeManager::HasTailCalls( EECodeInfo *pCodeInfo) { @@ -1431,8 +1422,7 @@ bool EECodeManager::EnumGcRefs( PREGDISPLAY pRD, DECODE_INTERRUPTIBILITY, curOffs ); - _ASSERTE(_gcInfoDecoder.IsInterruptible() || - (InterruptibleSafePointsEnabled() && _gcInfoDecoder.CouldBeInterruptibleSafePoint())); + _ASSERTE(_gcInfoDecoder.IsInterruptible() || _gcInfoDecoder.CouldBeSafePoint()); } #endif @@ -1535,7 +1525,7 @@ bool EECodeManager::EnumGcRefs( PREGDISPLAY pRD, curOffs - 1 ); - _ASSERTE((InterruptibleSafePointsEnabled() && gcInfoDecoder.CouldBeInterruptibleSafePoint())); + _ASSERTE(gcInfoDecoder.CouldBeSafePoint()); } } diff --git a/src/coreclr/vm/gccover.cpp b/src/coreclr/vm/gccover.cpp index 8403ab0837174c..3e3278de9ac726 100644 --- a/src/coreclr/vm/gccover.cpp +++ b/src/coreclr/vm/gccover.cpp @@ -552,8 +552,7 @@ void GCCoverageInfo::SprinkleBreakpoints( { case InstructionType::Call_IndirectUnconditional: #ifdef TARGET_AMD64 - if(!(EECodeManager::InterruptibleSafePointsEnabled() && safePointDecoder.AreSafePointsInterruptible()) && - safePointDecoder.IsSafePoint((UINT32)(cur + len - codeStart + regionOffsetAdj))) + if(!true) #endif { *(cur + writeableOffset) = INTERRUPT_INSTR_CALL; // return value. May need to protect @@ -564,8 +563,7 @@ void GCCoverageInfo::SprinkleBreakpoints( if(fGcStressOnDirectCalls.val(CLRConfig::INTERNAL_GcStressOnDirectCalls)) { #ifdef TARGET_AMD64 - if(!(EECodeManager::InterruptibleSafePointsEnabled() && safePointDecoder.AreSafePointsInterruptible()) && - safePointDecoder.IsSafePoint((UINT32)(cur + len - codeStart + regionOffsetAdj))) + if(!true) #endif { PBYTE nextInstr; @@ -717,7 +715,7 @@ void replaceSafePointInstructionWithGcStressInstr(GcInfoDecoder* decoder, UINT32 PBYTE instrPtr = (BYTE*)PCODEToPINSTR(pCode); // if this is an interruptible safe point, just replace it with an interrupt instr and we are done. - if (EECodeManager::InterruptibleSafePointsEnabled() && decoder->AreSafePointsInterruptible()) + if (true) { // The instruction about to be replaced cannot already be a gcstress instruction _ASSERTE(!IsGcCoverageInterruptInstruction(instrPtr)); diff --git a/src/coreclr/vm/gcinfodecoder.cpp b/src/coreclr/vm/gcinfodecoder.cpp index 097037fd8127c4..c9f9e73e3a9778 100644 --- a/src/coreclr/vm/gcinfodecoder.cpp +++ b/src/coreclr/vm/gcinfodecoder.cpp @@ -417,24 +417,14 @@ bool GcInfoDecoder::IsSafePoint() return m_SafePointIndex != m_NumSafePoints; } -bool GcInfoDecoder::AreSafePointsInterruptible() -{ - return m_Version >= 3; -} - -bool GcInfoDecoder::IsInterruptibleSafePoint() -{ - return IsSafePoint() && AreSafePointsInterruptible(); -} - -bool GcInfoDecoder::CouldBeInterruptibleSafePoint() +bool GcInfoDecoder::CouldBeSafePoint() { // This is used in asserts. Ideally it would return false // if current location canot possibly be a safepoint. // However in some cases we optimize away "boring" callsites when no variables are tracked. // So there is no way to tell precisely that a point is indeed not a safe point. // Thus we do what we can here, but this could be better if we could have more data - return AreSafePointsInterruptible() && m_NumInterruptibleRanges == 0; + return m_NumInterruptibleRanges == 0; } bool GcInfoDecoder::HasMethodDescGenericsInstContext() From dab93df6546aa568587a9a7f6b9b90db43341f1c Mon Sep 17 00:00:00 2001 From: vsadov <8218165+VSadov@users.noreply.github.com> Date: Sun, 22 Sep 2024 09:30:53 -0700 Subject: [PATCH 03/16] dead code --- src/coreclr/vm/gccover.cpp | 19 ------------------- src/coreclr/vm/gccover.h | 13 ------------- 2 files changed, 32 deletions(-) diff --git a/src/coreclr/vm/gccover.cpp b/src/coreclr/vm/gccover.cpp index 3e3278de9ac726..2fe47eff977c76 100644 --- a/src/coreclr/vm/gccover.cpp +++ b/src/coreclr/vm/gccover.cpp @@ -1778,25 +1778,6 @@ void DoGcStress (PCONTEXT regs, NativeCodeVersion nativeCodeVersion) enableWhenDone = true; } - -#if 0 - // TODO currently disabled. we only do a GC once per instruction location. - - /* note that for multiple threads, we can loose track and - forget to set reset the interrupt after we executed - an instruction, so some instruction points will not be - executed twice, but we still ge350t very good coverage - (perfect for single threaded cases) */ - - /* if we have not run this instruction in the past */ - /* remember to wack it to an INTERUPT_INSTR again */ - - if (!gcCover->IsBitSetForOffset(offset)) { - // gcCover->curInstr = instrPtr; - gcCover->SetBitForOffset(offset); - } -#endif // 0 - // // If we redirect for gc stress, we don't need this frame on the stack, // the redirection will push a resumable frame. diff --git a/src/coreclr/vm/gccover.h b/src/coreclr/vm/gccover.h index 7973efba5b0299..5ddf381af2c1ce 100644 --- a/src/coreclr/vm/gccover.h +++ b/src/coreclr/vm/gccover.h @@ -43,20 +43,7 @@ class GCCoverageInfo { // marshal over the bytes properly. }; - // Sloppy bitsets (will wrap, and not threadsafe) but best effort is OK - // since we just need half decent coverage. - BOOL IsBitSetForOffset(unsigned offset) { - unsigned dword = hasExecuted[(offset >> 5) % hasExecutedSize]; - return(dword & (1 << (offset & 0x1F))); - } - - void SetBitForOffset(unsigned offset) { - unsigned* dword = &hasExecuted[(offset >> 5) % hasExecutedSize]; - *dword |= (1 << (offset & 0x1F)) ; - } - void SprinkleBreakpoints(BYTE * saveAddr, PCODE codeStart, size_t codeSize, size_t regionOffsetAdj, BOOL fZapped); - }; typedef DPTR(GCCoverageInfo) PTR_GCCoverageInfo; // see code:GCCoverageInfo::savedCode From cc5d7d0338203b31ed748e8db6f2c6e41346097a Mon Sep 17 00:00:00 2001 From: vsadov <8218165+VSadov@users.noreply.github.com> Date: Sun, 22 Sep 2024 11:40:53 -0700 Subject: [PATCH 04/16] cleaned non-x86 path --- src/coreclr/vm/gccover.cpp | 1346 +++++++++++++++++------------------- src/coreclr/vm/gccover.h | 54 +- 2 files changed, 630 insertions(+), 770 deletions(-) diff --git a/src/coreclr/vm/gccover.cpp b/src/coreclr/vm/gccover.cpp index 2fe47eff977c76..de7dc06f36d50c 100644 --- a/src/coreclr/vm/gccover.cpp +++ b/src/coreclr/vm/gccover.cpp @@ -34,73 +34,6 @@ /****************************************************************************/ -MethodDesc* AsMethodDesc(size_t addr); -static PBYTE getTargetOfCall(PBYTE instrPtr, PCONTEXT regs, PBYTE*nextInstr); -#if defined(TARGET_ARM) || defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64) -static void replaceSafePointInstructionWithGcStressInstr(GcInfoDecoder* decoder, UINT32 safePointOffset, LPVOID codeStart); -static bool replaceInterruptibleRangesWithGcStressInstr (UINT32 startOffset, UINT32 stopOffset, LPVOID codeStart); -#endif - -// There is a call target instruction, try to find the MethodDesc for where target points to. -// Returns nullptr if it can't find it. -static MethodDesc* getTargetMethodDesc(PCODE target) -{ - MethodDesc* targetMD = ExecutionManager::GetCodeMethodDesc(target); - if (targetMD != nullptr) - { - // It is JIT/NGened call. - return targetMD; - } - - auto stubKind = RangeSectionStubManager::GetStubKind(target); - - if ((stubKind == STUB_CODE_BLOCK_VSD_DISPATCH_STUB) || - (stubKind == STUB_CODE_BLOCK_VSD_RESOLVE_STUB) || - (stubKind == STUB_CODE_BLOCK_VSD_LOOKUP_STUB) || - (stubKind == STUB_CODE_BLOCK_VSD_VTABLE_STUB)) - { - VirtualCallStubManager *pVSDStubManager = VirtualCallStubManager::FindStubManager(target, &stubKind); - if (pVSDStubManager != NULL) - { - // It is a VSD stub manager. - DispatchToken token(VirtualCallStubManager::GetTokenFromStubQuick(pVSDStubManager, target, stubKind)); - _ASSERTE(token.IsValid()); - return VirtualCallStubManager::GetInterfaceMethodDescFromToken(token); - } - } - - if (stubKind == STUB_CODE_BLOCK_PRECODE) - { - // The address looks like a value stub, try to get the method descriptor. - return MethodDesc::GetMethodDescFromStubAddr(target, TRUE); - } - - if (stubKind == STUB_CODE_BLOCK_STUBPRECODE) - { - return (MethodDesc*)((StubPrecode*)PCODEToPINSTR(target))->GetMethodDesc(); - } - - if (stubKind == STUB_CODE_BLOCK_FIXUPPRECODE) - { - if (!FixupPrecode::IsFixupPrecodeByASM(target)) - { - // If the target slot points to the fixup part of the stub, the actual - // stub starts FixupPrecode::FixupCodeOffset bytes below the target, - // so we need to compensate for it. - target -= FixupPrecode::FixupCodeOffset; - if (!FixupPrecode::IsFixupPrecodeByASM(target)) - { - _ASSERTE(!"Invalid FixupPrecode address"); // We should never get other precode type here - return nullptr; - } - } - - return (MethodDesc*)((FixupPrecode*)PCODEToPINSTR(target))->GetMethodDesc(); - } - - return nullptr; -} - bool IsGcCoverageInterruptInstruction(PBYTE instrPtr) { UINT32 instrVal; @@ -181,8 +114,8 @@ void SetupAndSprinkleBreakpoints( 0, fZapped); - // This is not required for ARM* as the above call does the work for both hot & cold regions -#if !defined(TARGET_ARM) && !defined(TARGET_ARM64) && !defined(TARGET_LOONGARCH64) && !defined(TARGET_RISCV64) + // This is only required for X86, since otherwise the above call does the work for both hot & cold regions +#if defined(TARGET_X86) if (gcCover->methodRegion.coldSize != 0) { gcCover->SprinkleBreakpoints(gcCover->savedCode + gcCover->methodRegion.hotSize, @@ -261,6 +194,81 @@ void SetupGcCoverage(NativeCodeVersion nativeCodeVersion, BYTE* methodStartPtr) SetupAndSprinkleBreakpointsForJittedMethod(nativeCodeVersion, codeStart); } +// There are some code path in DoGcStress to return without doing a GC but we +// now relies on EE suspension to update the GC STRESS instruction. +// We need to do a extra EE suspension/resume even without GC. +FORCEINLINE void UpdateGCStressInstructionWithoutGC () +{ + ThreadSuspend::SuspendEE(ThreadSuspend::SUSPEND_OTHER); + ThreadSuspend::RestartEE(TRUE, TRUE); +} + +#if defined(TARGET_X86) +///////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////// + +static PBYTE getTargetOfCall(PBYTE instrPtr, PCONTEXT regs, PBYTE*nextInstr); + +// There is a call target instruction, try to find the MethodDesc for where target points to. +// Returns nullptr if it can't find it. +static MethodDesc* getTargetMethodDesc(PCODE target) +{ + MethodDesc* targetMD = ExecutionManager::GetCodeMethodDesc(target); + if (targetMD != nullptr) + { + // It is JIT/NGened call. + return targetMD; + } + + auto stubKind = RangeSectionStubManager::GetStubKind(target); + + if ((stubKind == STUB_CODE_BLOCK_VSD_DISPATCH_STUB) || + (stubKind == STUB_CODE_BLOCK_VSD_RESOLVE_STUB) || + (stubKind == STUB_CODE_BLOCK_VSD_LOOKUP_STUB) || + (stubKind == STUB_CODE_BLOCK_VSD_VTABLE_STUB)) + { + VirtualCallStubManager *pVSDStubManager = VirtualCallStubManager::FindStubManager(target, &stubKind); + if (pVSDStubManager != NULL) + { + // It is a VSD stub manager. + DispatchToken token(VirtualCallStubManager::GetTokenFromStubQuick(pVSDStubManager, target, stubKind)); + _ASSERTE(token.IsValid()); + return VirtualCallStubManager::GetInterfaceMethodDescFromToken(token); + } + } + + if (stubKind == STUB_CODE_BLOCK_PRECODE) + { + // The address looks like a value stub, try to get the method descriptor. + return MethodDesc::GetMethodDescFromStubAddr(target, TRUE); + } + + if (stubKind == STUB_CODE_BLOCK_STUBPRECODE) + { + return (MethodDesc*)((StubPrecode*)PCODEToPINSTR(target))->GetMethodDesc(); + } + + if (stubKind == STUB_CODE_BLOCK_FIXUPPRECODE) + { + if (!FixupPrecode::IsFixupPrecodeByASM(target)) + { + // If the target slot points to the fixup part of the stub, the actual + // stub starts FixupPrecode::FixupCodeOffset bytes below the target, + // so we need to compensate for it. + target -= FixupPrecode::FixupCodeOffset; + if (!FixupPrecode::IsFixupPrecodeByASM(target)) + { + _ASSERTE(!"Invalid FixupPrecode address"); // We should never get other precode type here + return nullptr; + } + } + + return (MethodDesc*)((FixupPrecode*)PCODEToPINSTR(target))->GetMethodDesc(); + } + + return nullptr; +} + void ReplaceInstrAfterCall(PBYTE instrToReplace, MethodDesc* callMD) { ReturnKind returnKind = callMD->GetReturnKind(true); @@ -354,97 +362,6 @@ void ReplaceInstrAfterCall(PBYTE instrToReplace, MethodDesc* callMD) #endif } -#ifdef TARGET_AMD64 - -class GCCoverageRangeEnumerator -{ -private: - - ICodeManager *m_pCodeManager; - GCInfoToken m_pvGCTable; - BYTE *m_codeStart; - BYTE *m_codeEnd; - BYTE *m_curFuncletEnd; - BYTE *m_nextFunclet; - - - BYTE* GetNextFunclet () - { - if (m_nextFunclet == NULL) - return m_codeEnd; - - BYTE *pCurFunclet = (BYTE*)EECodeInfo::findNextFunclet(m_nextFunclet, m_codeEnd - m_nextFunclet, (LPVOID*)&m_curFuncletEnd); - m_nextFunclet = (pCurFunclet != NULL) ? m_curFuncletEnd : NULL; - - if (pCurFunclet == NULL) - return m_codeEnd; - - LOG((LF_JIT, LL_INFO1000, "funclet range %p-%p\n", pCurFunclet, m_curFuncletEnd)); - - // - // workaround - adjust the funclet end address to exclude uninterruptible - // code at the end of each funclet. The jit currently puts data like - // jump tables in the code portion of the allocation, instead of the - // read-only portion. - // - // TODO: If the entire range is uninterruptible, we should skip the - // entire funclet. - // - unsigned ofsLastInterruptible = m_pCodeManager->FindEndOfLastInterruptibleRegion( - static_cast(pCurFunclet - m_codeStart), - static_cast(m_curFuncletEnd - m_codeStart), - m_pvGCTable); - - if (ofsLastInterruptible) - { - m_curFuncletEnd = m_codeStart + ofsLastInterruptible; - LOG((LF_JIT, LL_INFO1000, "adjusted end to %p\n", m_curFuncletEnd)); - } - - return pCurFunclet; - } - - -public: - - GCCoverageRangeEnumerator (ICodeManager *pCodeManager, GCInfoToken pvGCTable, BYTE *codeStart, SIZE_T codeSize) - { - m_pCodeManager = pCodeManager; - m_pvGCTable = pvGCTable; - m_codeStart = codeStart; - m_codeEnd = codeStart + codeSize; - m_nextFunclet = codeStart; - - GetNextFunclet(); - } - - // Checks that the given pointer is inside of a range where gc should be - // tested. If not, increments the pointer until it is, and returns the - // new pointer. - BYTE *EnsureInRange (BYTE *cur) - { - if (cur >= m_curFuncletEnd) - { - cur = GetNextFunclet(); - } - - return cur; - } - - BYTE *SkipToNextRange () - { - return GetNextFunclet(); - } -}; - -#endif // TARGET_AMD64 - -/****************************************************************************/ -/* sprinkle interrupt instructions that will stop on every GCSafe location - regionOffsetAdj - Represents the offset of the current region - from the beginning of the method (is 0 for hot region) -*/ - void GCCoverageInfo::SprinkleBreakpoints( BYTE * saveAddr, PCODE pCode, @@ -678,352 +595,46 @@ void GCCoverageInfo::SprinkleBreakpoints( #endif // TARGET_X86 } -#if defined(TARGET_ARM) || defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64) - -#ifdef TARGET_RISCV64 -enum +static size_t getRegVal(unsigned regNum, PCONTEXT regs) { - REG_RA = 1, - JAL = 0x6f, - JALR = 0x67, -}; -#endif + return *getRegAddr(regNum, regs); +} -#ifdef PARTIALLY_INTERRUPTIBLE_GC_SUPPORTED +/****************************************************************************/ +static PBYTE getTargetOfCall(PBYTE instrPtr, PCONTEXT regs, PBYTE* nextInstr) { -void replaceSafePointInstructionWithGcStressInstr(GcInfoDecoder* decoder, UINT32 safePointOffset, LPVOID pGCCover) -{ - PCODE pCode = (PCODE)NULL; - IJitManager::MethodRegionInfo *ptr = &(((GCCoverageInfo*)pGCCover)->methodRegion); + BYTE sibindexadj = 0; + BYTE baseadj = 0; + WORD displace = 0; - //Get code address from offset - if (safePointOffset < ptr->hotSize) - pCode = ptr->hotStartAddress + safePointOffset; - else if(safePointOffset - ptr->hotSize < ptr->coldSize) - { - SIZE_T coldOffset = safePointOffset - ptr->hotSize; - pCode = ptr->coldStartAddress + coldOffset; - } - else - { - //For some methods( eg MCCTest.MyClass.GetSum2 in test file jit\jit64\mcc\interop\mcc_i07.il) gcinfo points to a safepoint - //beyond the length of the method. So commenting the below assert. - //_ASSERTE(safePointOffset - ptr->hotSize < ptr->coldSize); - return; - } + // In certain situations, the instruction bytes are read from a different + // location than the actual bytes being executed. + // When decoding the instructions of a method which is sprinkled with + // TRAP instructions for GCStress, we decode the bytes from a copy + // of the instructions stored before the traps-for-gc were inserted. + // However, the PC-relative addressing/displacement of the CALL-target + // will still be with respect to the currently executing PC. + // So, if a register context is available, we pick the PC from it + // (for address calculation purposes only). - PBYTE instrPtr = (BYTE*)PCODEToPINSTR(pCode); + PBYTE PC = (regs) ? (PBYTE)GetIP(regs) : instrPtr; - // if this is an interruptible safe point, just replace it with an interrupt instr and we are done. - if (true) +#ifdef TARGET_ARM + if((instrPtr[1] & 0xf0) == 0xf0) // direct call { - // The instruction about to be replaced cannot already be a gcstress instruction - _ASSERTE(!IsGcCoverageInterruptInstruction(instrPtr)); - - ExecutableWriterHolder instrPtrWriterHolder(instrPtr, sizeof(DWORD)); -#if defined(TARGET_ARM) - size_t instrLen = GetARMInstructionLength(instrPtr); - - if (instrLen == 2) - *((WORD*)instrPtrWriterHolder.GetRW()) = INTERRUPT_INSTR; - else - { - *((DWORD*)instrPtrWriterHolder.GetRW()) = INTERRUPT_INSTR_32; - } -#elif defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64) - *((DWORD*)instrPtrWriterHolder.GetRW()) = INTERRUPT_INSTR; -#endif // TARGET_XXXX_ - return; + int imm32 = GetThumb2BlRel24((UINT16 *)instrPtr); + *nextInstr = instrPtr + 4; + return PC + 4 + imm32; } - - // For code sequences of the type - // BL func1 - // BL func2 // Safe point 1 - // mov r1 r0 // Safe point 2 - // Both the above safe points instruction must be replaced with gcStress instruction. - // However as the first safe point is already replaced with gcstress instruction, decoding of the call - // instruction will fail when processing for the 2nd safe point. Therefore saved instruction must be used instead of - // instrPtr for decoding the call instruction. - PBYTE savedInstrPtr = ((GCCoverageInfo*)pGCCover)->savedCode + safePointOffset; - - //Determine if instruction before the safe point is call using immediate (BLX Imm) or call by register (BLX Rm) - BOOL instructionIsACallThroughRegister = FALSE; - BOOL instructionIsACallThroughImmediate = FALSE; - -#if defined(TARGET_ARM) - - // POSSIBLE BUG: Note that we are looking backwards by 2 or 4 bytes, looking for particular call instruction encodings. - // However, we don't know if the previous instruction is 2 bytes or 4 bytes. Looking back 2 bytes could be looking into - // the middle of a 4-byte instruction. The only safe way to do this is by walking forward from the first instruction of - // the function. - - // call by register instruction is two bytes (BL Reg T1 encoding) - WORD instr = *((WORD*)savedInstrPtr - 1); - instr = instr & 0xff87; - if ((instr ^ 0x4780) == 0) + else if(((instrPtr[1] & 0x47) == 0x47) & ((instrPtr[0] & 0x80) == 0x80)) // indirect call { - // It is call by register - instructionIsACallThroughRegister = TRUE; + *nextInstr = instrPtr + 2; + unsigned int regnum = (instrPtr[0] & 0x78) >> 3; + return (BYTE *)getRegVal(regnum, regs); } else { - // call using immediate instructions are 4 bytes (BL