From d6bc5b823debc95206b2b41b3a178d6909648c5e Mon Sep 17 00:00:00 2001 From: Jan Vorlicek Date: Fri, 13 Mar 2026 16:51:03 +0100 Subject: [PATCH 1/7] Fix most of the diagnostic EH tests with interpreter Before this change, all of the exception handling diagnostic tests were failing. There were couple of reasons: * The debugger stack walk didn't work when it needed to extract the starting context from explicit frames and the InterpreterFrame was the first one. It got the context of the native caller of the interpreter instead of the interpreter context. * Exception interception was not supported for the interpreted code in the runtime yet * The IL to native offsets map generated by the interpreter compiler was always setting the source field to ICorDebugInfo::SOURCE_TYPE_INVALID. Exception interception looks for offset with ICorDebugInfo::STACK_EMPTY and thus it found none and has failed. * The System.Runtime.StackFrameIterator methods were not ignored by the DacDbiInterfaceImpl::UnwindStackWalkFrame and so the tests verifying the stack trace were failing * InterpreterFrame was not ignored when enumerating internal frames, generating extra unexpected stuff to the debugger stack trace. This change fixes these issues and now only three of the total 21 diagnostic EH tests are failing. It also fixes an issue introduced by recent refactoring of the debugging code error handling that was causing the tests to fail on Linux even without interpreter. --- src/coreclr/debug/daccess/dacdbiimpl.cpp | 11 ++++++++++- .../debug/daccess/dacdbiimplstackwalk.cpp | 19 ++++++++++++++++++- src/coreclr/interpreter/compiler.cpp | 3 ++- src/coreclr/interpreter/compiler.h | 1 + src/coreclr/vm/exceptionhandling.cpp | 7 ++++--- src/coreclr/vm/frames.h | 6 ++++++ 6 files changed, 41 insertions(+), 6 deletions(-) diff --git a/src/coreclr/debug/daccess/dacdbiimpl.cpp b/src/coreclr/debug/daccess/dacdbiimpl.cpp index 015168aa6494fa..7e3694b7bb53a1 100644 --- a/src/coreclr/debug/daccess/dacdbiimpl.cpp +++ b/src/coreclr/debug/daccess/dacdbiimpl.cpp @@ -6084,8 +6084,17 @@ HRESULT DacDbiInterfaceImpl::GetContext(VMPTR_Thread vmThread, DT_CONTEXT * pCon // that has context available for stackwalking (SP and PC) // For example: RedirectedThreadFrame, InlinedCallFrame, DynamicHelperFrame, CLRToCOMMethodFrame Frame *frame = pThread->GetFrame(); + while (frame != NULL && frame != FRAME_TOP) { + if (frame->GetFrameIdentifier() == FrameIdentifier::InterpreterFrame) + { + PTR_InterpreterFrame pInterpreterFrame = dac_cast(pThread->GetFrame()); + pInterpreterFrame->SetContextToInterpMethodContextFrame(&tmpContext); + CopyMemory(pContextBuffer, &tmpContext, sizeof(*pContextBuffer)); + return S_OK; + } + frame->UpdateRegDisplay(&tmpRd); if (GetRegdisplaySP(&tmpRd) != 0 && GetControlPC(&tmpRd) != 0) { @@ -6097,7 +6106,7 @@ HRESULT DacDbiInterfaceImpl::GetContext(VMPTR_Thread vmThread, DT_CONTEXT * pCon // DT_CONTEXT_CONTROL already includes the frame register for X86 and ARM64 architectures #endif ; - return hr; + return S_OK; } frame = frame->Next(); } diff --git a/src/coreclr/debug/daccess/dacdbiimplstackwalk.cpp b/src/coreclr/debug/daccess/dacdbiimplstackwalk.cpp index 50a8771a21a6f1..3f20448b35b4d4 100644 --- a/src/coreclr/debug/daccess/dacdbiimplstackwalk.cpp +++ b/src/coreclr/debug/daccess/dacdbiimplstackwalk.cpp @@ -301,7 +301,8 @@ HRESULT DacDbiInterfaceImpl::UnwindStackWalkFrame(StackWalkHandle pSFIHandle, OU MethodDesc *pMD = pIter->m_crawl.GetFunction(); // EH.DispatchEx, EH.RhThrowEx, EH.RhThrowHwEx, ExceptionServices.InternalCalls.SfiInit, ExceptionServices.InternalCalls.SfiNext - if (pMD->GetMethodTable() == g_pEHClass || pMD->GetMethodTable() == g_pExceptionServicesInternalCallsClass) + // and System.Runtime.StackFrameIterator.* + if (pMD->GetMethodTable() == g_pEHClass || pMD->GetMethodTable() == g_pExceptionServicesInternalCallsClass || pMD->GetMethodTable() == g_pStackFrameIteratorClass) { continue; } @@ -517,6 +518,14 @@ HRESULT DacDbiInterfaceImpl::GetCountOfInternalFrames(VMPTR_Thread vmThread, OUT continue; } } + + if (pFrame->GetFrameIdentifier() == FrameIdentifier::InterpreterFrame) + { + // Skip InterpreterFrame + pFrame = pFrame->Next(); + continue; + } + CorDebugInternalFrameType ift = GetInternalFrameType(pFrame); if (ift != STUBFRAME_NONE) { @@ -573,6 +582,14 @@ HRESULT DacDbiInterfaceImpl::EnumerateInternalFrames(VMPTR_Thread vmThread, FP_I continue; } } + + if (pFrame->GetFrameIdentifier() == FrameIdentifier::InterpreterFrame) + { + // Skip InterpreterFrame + pFrame = pFrame->Next(); + continue; + } + // check if the internal frame is interesting frameData.stubFrame.frameType = GetInternalFrameType(pFrame); if (frameData.stubFrame.frameType != STUBFRAME_NONE) diff --git a/src/coreclr/interpreter/compiler.cpp b/src/coreclr/interpreter/compiler.cpp index 10c649fa958f13..569c3a2d7f1cb6 100644 --- a/src/coreclr/interpreter/compiler.cpp +++ b/src/coreclr/interpreter/compiler.cpp @@ -319,6 +319,7 @@ InterpInst* InterpCompiler::NewIns(int opcode, int dataLen) InterpInst *ins = (InterpInst*)getAllocator(IMK_Instruction).allocateZeroed(insSize); ins->opcode = opcode; ins->ilOffset = m_currentILOffset; + ins->stackDepth = m_pStackPointer - m_pStackBase; m_pLastNewIns = ins; return ins; } @@ -1069,7 +1070,7 @@ int32_t* InterpCompiler::EmitCodeIns(int32_t *ip, InterpInst *ins, TArraystackDepth == 0 ? ICorDebugInfo::STACK_EMPTY : ICorDebugInfo::SOURCE_TYPE_INVALID; m_ILToNativeMapSize++; } } diff --git a/src/coreclr/interpreter/compiler.h b/src/coreclr/interpreter/compiler.h index 6f58d0c065cf40..91c9863825ecd9 100644 --- a/src/coreclr/interpreter/compiler.h +++ b/src/coreclr/interpreter/compiler.h @@ -252,6 +252,7 @@ struct InterpInst uint32_t flags; int32_t dVar; int32_t sVars[3]; // Currently all instructions have at most 3 sregs + int32_t stackDepth; int32_t data[]; diff --git a/src/coreclr/vm/exceptionhandling.cpp b/src/coreclr/vm/exceptionhandling.cpp index 6b9d52189dac84..c2cd5cfb28683f 100644 --- a/src/coreclr/vm/exceptionhandling.cpp +++ b/src/coreclr/vm/exceptionhandling.cpp @@ -3188,7 +3188,7 @@ void CallCatchFunclet(OBJECTREF throwable, BYTE* pHandlerIP, REGDISPLAY* pvRegDi pThread->GetExceptionState()->GetDebuggerState()->GetDebuggerInterceptInfo(&pInterceptMD, NULL, (PBYTE*)&(sfInterceptStackFrame.SP), &ulRelOffset, NULL); if (sfInterceptStackFrame.SP == GetSP(pvRegDisplay->pCurrentContext)) { - PCODE pStartAddress = pInterceptMD->GetNativeCode(); + PCODE pStartAddress = pInterceptMD->GetCodeForInterpreterOrJitted(); EECodeInfo codeInfo(pStartAddress); _ASSERTE(codeInfo.IsValid()); @@ -3226,7 +3226,8 @@ void CallCatchFunclet(OBJECTREF throwable, BYTE* pHandlerIP, REGDISPLAY* pvRegDi { if (fIntercepted) { - ClrRestoreNonvolatileContext(pvRegDisplay->pCurrentContext, targetSSP); + EECodeInfo codeInfo(GetIP(pvRegDisplay->pCurrentContext)); + codeInfo.GetCodeManager()->ResumeAfterCatch(pvRegDisplay->pCurrentContext, targetSSP, /* fIntercepted */ true); } #ifdef HOST_UNIX if (propagateExceptionCallback) @@ -3338,7 +3339,7 @@ void ResumeAtInterceptionLocation(REGDISPLAY* pvRegDisplay) SetIP(pvRegDisplay->pCurrentContext, uResumePC); STRESS_LOG2(LF_EH, LL_INFO100, "Resuming at interception location at IP=%p, SP=%p\n", uResumePC, GetSP(pvRegDisplay->pCurrentContext)); - ClrRestoreNonvolatileContext(pvRegDisplay->pCurrentContext, targetSSP); + codeInfo.GetCodeManager()->ResumeAfterCatch(pvRegDisplay->pCurrentContext, targetSSP, /* fIntercepted */ true); } void CallFinallyFunclet(BYTE* pHandlerIP, REGDISPLAY* pvRegDisplay, ExInfo* exInfo) diff --git a/src/coreclr/vm/frames.h b/src/coreclr/vm/frames.h index 6c60cc14fa94bd..55072025229b0f 100644 --- a/src/coreclr/vm/frames.h +++ b/src/coreclr/vm/frames.h @@ -2560,6 +2560,12 @@ class InterpreterFrame : public FramedMethodFrame m_isFaulting = isFaulting; } + Interception GetInterception_Impl() + { + LIMITED_METHOD_DAC_CONTRACT; + return m_isFaulting ? INTERCEPTION_EXCEPTION : INTERCEPTION_NONE; + } + void GcScanRoots_Impl(promote_func *fn, ScanContext* sc) { fn(dac_cast(dac_cast(&m_continuation)), sc, 0); From 2874951c5db2ec7d0c64785300f0d1a51e3d38f7 Mon Sep 17 00:00:00 2001 From: Jan Vorlicek Date: Mon, 16 Mar 2026 16:59:24 +0100 Subject: [PATCH 2/7] PR feedback and build fix --- src/coreclr/debug/daccess/dacdbiimpl.cpp | 3 ++- src/coreclr/debug/daccess/dacdbiimplstackwalk.cpp | 4 ++++ src/coreclr/interpreter/compiler.cpp | 8 ++++++-- src/coreclr/interpreter/compiler.h | 7 ++++--- 4 files changed, 16 insertions(+), 6 deletions(-) diff --git a/src/coreclr/debug/daccess/dacdbiimpl.cpp b/src/coreclr/debug/daccess/dacdbiimpl.cpp index 7e3694b7bb53a1..c95d26fb09812d 100644 --- a/src/coreclr/debug/daccess/dacdbiimpl.cpp +++ b/src/coreclr/debug/daccess/dacdbiimpl.cpp @@ -6087,6 +6087,7 @@ HRESULT DacDbiInterfaceImpl::GetContext(VMPTR_Thread vmThread, DT_CONTEXT * pCon while (frame != NULL && frame != FRAME_TOP) { +#ifdef FEATURE_INTERPRETER if (frame->GetFrameIdentifier() == FrameIdentifier::InterpreterFrame) { PTR_InterpreterFrame pInterpreterFrame = dac_cast(pThread->GetFrame()); @@ -6094,7 +6095,7 @@ HRESULT DacDbiInterfaceImpl::GetContext(VMPTR_Thread vmThread, DT_CONTEXT * pCon CopyMemory(pContextBuffer, &tmpContext, sizeof(*pContextBuffer)); return S_OK; } - +#endif // FEATURE_INTERPRETER frame->UpdateRegDisplay(&tmpRd); if (GetRegdisplaySP(&tmpRd) != 0 && GetControlPC(&tmpRd) != 0) { diff --git a/src/coreclr/debug/daccess/dacdbiimplstackwalk.cpp b/src/coreclr/debug/daccess/dacdbiimplstackwalk.cpp index 3f20448b35b4d4..82eb4ec9f1db60 100644 --- a/src/coreclr/debug/daccess/dacdbiimplstackwalk.cpp +++ b/src/coreclr/debug/daccess/dacdbiimplstackwalk.cpp @@ -519,12 +519,14 @@ HRESULT DacDbiInterfaceImpl::GetCountOfInternalFrames(VMPTR_Thread vmThread, OUT } } +#ifdef FEATURE_INTERPRETER if (pFrame->GetFrameIdentifier() == FrameIdentifier::InterpreterFrame) { // Skip InterpreterFrame pFrame = pFrame->Next(); continue; } +#endif // FEATURE_INTERPRETER CorDebugInternalFrameType ift = GetInternalFrameType(pFrame); if (ift != STUBFRAME_NONE) @@ -583,12 +585,14 @@ HRESULT DacDbiInterfaceImpl::EnumerateInternalFrames(VMPTR_Thread vmThread, FP_I } } +#ifdef FEATURE_INTERPRETER if (pFrame->GetFrameIdentifier() == FrameIdentifier::InterpreterFrame) { // Skip InterpreterFrame pFrame = pFrame->Next(); continue; } +#endif // FEATURE_INTERPRETER // check if the internal frame is interesting frameData.stubFrame.frameType = GetInternalFrameType(pFrame); diff --git a/src/coreclr/interpreter/compiler.cpp b/src/coreclr/interpreter/compiler.cpp index 569c3a2d7f1cb6..719c82bb27f93e 100644 --- a/src/coreclr/interpreter/compiler.cpp +++ b/src/coreclr/interpreter/compiler.cpp @@ -319,7 +319,11 @@ InterpInst* InterpCompiler::NewIns(int opcode, int dataLen) InterpInst *ins = (InterpInst*)getAllocator(IMK_Instruction).allocateZeroed(insSize); ins->opcode = opcode; ins->ilOffset = m_currentILOffset; - ins->stackDepth = m_pStackPointer - m_pStackBase; + if ((m_pLastNewIns != nullptr) && (m_pLastNewIns->ilOffset != m_currentILOffset)) + { + // This is the first instruction we have emitted for this IL offset, so set the flag. + ins->flags |= INTERP_INST_FLAG_FIRST_FOR_IL_OP; + } m_pLastNewIns = ins; return ins; } @@ -1070,7 +1074,7 @@ int32_t* InterpCompiler::EmitCodeIns(int32_t *ip, InterpInst *ins, TArraystackDepth == 0 ? ICorDebugInfo::STACK_EMPTY : ICorDebugInfo::SOURCE_TYPE_INVALID; + m_pILToNativeMap[m_ILToNativeMapSize].source = (ins->flags & INTERP_INST_FLAG_FIRST_FOR_IL_OP) ? ICorDebugInfo::STACK_EMPTY : ICorDebugInfo::SOURCE_TYPE_INVALID; m_ILToNativeMapSize++; } } diff --git a/src/coreclr/interpreter/compiler.h b/src/coreclr/interpreter/compiler.h index 91c9863825ecd9..00f3fdf840c378 100644 --- a/src/coreclr/interpreter/compiler.h +++ b/src/coreclr/interpreter/compiler.h @@ -233,7 +233,9 @@ enum InterpInstFlags { INTERP_INST_FLAG_CALL = 0x01, // Flag used internally by the var offset allocator - INTERP_INST_FLAG_ACTIVE_CALL = 0x02 + INTERP_INST_FLAG_ACTIVE_CALL = 0x02, + // First interpreter instruction for an IL opcode + INTERP_INST_FLAG_FIRST_FOR_IL_OP = 0x04 }; struct InterpInst @@ -252,7 +254,6 @@ struct InterpInst uint32_t flags; int32_t dVar; int32_t sVars[3]; // Currently all instructions have at most 3 sregs - int32_t stackDepth; int32_t data[]; @@ -820,7 +821,7 @@ class InterpCompiler private: // Instructions InterpBasicBlock *m_pCBB, *m_pEntryBB; - InterpInst* m_pLastNewIns; + InterpInst* m_pLastNewIns = nullptr; int32_t GetInsLength(InterpInst *pIns); bool InsIsNop(InterpInst *pIns); From 4b09e65f9e66e1cc1ae95a3fe3fb65692b7f5f9a Mon Sep 17 00:00:00 2001 From: Jan Vorlicek Date: Mon, 16 Mar 2026 19:08:08 +0100 Subject: [PATCH 3/7] PR feedback --- src/coreclr/interpreter/compiler.cpp | 2 +- src/coreclr/vm/exceptionhandling.cpp | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/coreclr/interpreter/compiler.cpp b/src/coreclr/interpreter/compiler.cpp index 719c82bb27f93e..6e94941638b0d0 100644 --- a/src/coreclr/interpreter/compiler.cpp +++ b/src/coreclr/interpreter/compiler.cpp @@ -319,7 +319,7 @@ InterpInst* InterpCompiler::NewIns(int opcode, int dataLen) InterpInst *ins = (InterpInst*)getAllocator(IMK_Instruction).allocateZeroed(insSize); ins->opcode = opcode; ins->ilOffset = m_currentILOffset; - if ((m_pLastNewIns != nullptr) && (m_pLastNewIns->ilOffset != m_currentILOffset)) + if ((m_pLastNewIns == nullptr) || (m_pLastNewIns->ilOffset != m_currentILOffset)) { // This is the first instruction we have emitted for this IL offset, so set the flag. ins->flags |= INTERP_INST_FLAG_FIRST_FOR_IL_OP; diff --git a/src/coreclr/vm/exceptionhandling.cpp b/src/coreclr/vm/exceptionhandling.cpp index c2cd5cfb28683f..96eacf9fa1aa8e 100644 --- a/src/coreclr/vm/exceptionhandling.cpp +++ b/src/coreclr/vm/exceptionhandling.cpp @@ -3227,6 +3227,7 @@ void CallCatchFunclet(OBJECTREF throwable, BYTE* pHandlerIP, REGDISPLAY* pvRegDi if (fIntercepted) { EECodeInfo codeInfo(GetIP(pvRegDisplay->pCurrentContext)); + _ASSERTE(codeInfo.IsValid()); codeInfo.GetCodeManager()->ResumeAfterCatch(pvRegDisplay->pCurrentContext, targetSSP, /* fIntercepted */ true); } #ifdef HOST_UNIX From 2bc9b434c4ce7abf156f261b3e24d2dd3a7e9943 Mon Sep 17 00:00:00 2001 From: Jan Vorlicek Date: Mon, 16 Mar 2026 21:43:49 +0100 Subject: [PATCH 4/7] PR feedback - Fix the empty stack detection --- src/coreclr/interpreter/compiler.cpp | 10 ++++++---- src/coreclr/interpreter/compiler.h | 4 ++-- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/src/coreclr/interpreter/compiler.cpp b/src/coreclr/interpreter/compiler.cpp index 6e94941638b0d0..c07ecae5512161 100644 --- a/src/coreclr/interpreter/compiler.cpp +++ b/src/coreclr/interpreter/compiler.cpp @@ -319,10 +319,12 @@ InterpInst* InterpCompiler::NewIns(int opcode, int dataLen) InterpInst *ins = (InterpInst*)getAllocator(IMK_Instruction).allocateZeroed(insSize); ins->opcode = opcode; ins->ilOffset = m_currentILOffset; - if ((m_pLastNewIns == nullptr) || (m_pLastNewIns->ilOffset != m_currentILOffset)) + ptrdiff_t stackDepth = m_pStackPointer - m_pStackBase; + if ((stackDepth == 0) && ((m_pLastNewIns == nullptr) || (m_pLastNewIns->ilOffset != m_currentILOffset))) { - // This is the first instruction we have emitted for this IL offset, so set the flag. - ins->flags |= INTERP_INST_FLAG_FIRST_FOR_IL_OP; + // This is the first instruction we are emitting for this IL offset and the stack is empty, which + // implies the IL stack is empty too. + ins->flags |= INTERP_INST_FLAG_EMPTY_IL_STACK; } m_pLastNewIns = ins; return ins; @@ -1074,7 +1076,7 @@ int32_t* InterpCompiler::EmitCodeIns(int32_t *ip, InterpInst *ins, TArrayflags & INTERP_INST_FLAG_FIRST_FOR_IL_OP) ? ICorDebugInfo::STACK_EMPTY : ICorDebugInfo::SOURCE_TYPE_INVALID; + m_pILToNativeMap[m_ILToNativeMapSize].source = (ins->flags & INTERP_INST_FLAG_EMPTY_IL_STACK) ? ICorDebugInfo::STACK_EMPTY : ICorDebugInfo::SOURCE_TYPE_INVALID; m_ILToNativeMapSize++; } } diff --git a/src/coreclr/interpreter/compiler.h b/src/coreclr/interpreter/compiler.h index 00f3fdf840c378..10ee3d9722e7c3 100644 --- a/src/coreclr/interpreter/compiler.h +++ b/src/coreclr/interpreter/compiler.h @@ -234,8 +234,8 @@ enum InterpInstFlags INTERP_INST_FLAG_CALL = 0x01, // Flag used internally by the var offset allocator INTERP_INST_FLAG_ACTIVE_CALL = 0x02, - // First interpreter instruction for an IL opcode - INTERP_INST_FLAG_FIRST_FOR_IL_OP = 0x04 + // The IL stack is empty at this instruction + INTERP_INST_FLAG_EMPTY_IL_STACK = 0x04 }; struct InterpInst From 8d7dd9b16200b92d71103b26279b94a2cd1bdbca Mon Sep 17 00:00:00 2001 From: Jan Vorlicek Date: Mon, 16 Mar 2026 22:12:09 +0100 Subject: [PATCH 5/7] Fix one more place missing GetCodeForInterpreterOrJitted --- src/coreclr/vm/exceptionhandling.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coreclr/vm/exceptionhandling.cpp b/src/coreclr/vm/exceptionhandling.cpp index 96eacf9fa1aa8e..fb8a9fef5b0171 100644 --- a/src/coreclr/vm/exceptionhandling.cpp +++ b/src/coreclr/vm/exceptionhandling.cpp @@ -3327,7 +3327,7 @@ void ResumeAtInterceptionLocation(REGDISPLAY* pvRegDisplay) ExInfo::PopExInfos(pThread, (void*)targetSp); - PCODE pStartAddress = pInterceptMD->GetNativeCode(); + PCODE pStartAddress = pInterceptMD->GetCodeForInterpreterOrJitted(); EECodeInfo codeInfo(pStartAddress); _ASSERTE(codeInfo.IsValid()); From 2fa3740281a0017a2616d5e2b5de6e61e0b90cc6 Mon Sep 17 00:00:00 2001 From: Jan Vorlicek Date: Mon, 16 Mar 2026 23:29:29 +0100 Subject: [PATCH 6/7] Copilot feedback - use frame local directly --- src/coreclr/debug/daccess/dacdbiimpl.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coreclr/debug/daccess/dacdbiimpl.cpp b/src/coreclr/debug/daccess/dacdbiimpl.cpp index c95d26fb09812d..b60766b8fb9ac7 100644 --- a/src/coreclr/debug/daccess/dacdbiimpl.cpp +++ b/src/coreclr/debug/daccess/dacdbiimpl.cpp @@ -6090,7 +6090,7 @@ HRESULT DacDbiInterfaceImpl::GetContext(VMPTR_Thread vmThread, DT_CONTEXT * pCon #ifdef FEATURE_INTERPRETER if (frame->GetFrameIdentifier() == FrameIdentifier::InterpreterFrame) { - PTR_InterpreterFrame pInterpreterFrame = dac_cast(pThread->GetFrame()); + PTR_InterpreterFrame pInterpreterFrame = dac_cast(frame); pInterpreterFrame->SetContextToInterpMethodContextFrame(&tmpContext); CopyMemory(pContextBuffer, &tmpContext, sizeof(*pContextBuffer)); return S_OK; From f982242e4c144cf85b471396f6906616fb059868 Mon Sep 17 00:00:00 2001 From: Jan Vorlicek Date: Tue, 17 Mar 2026 10:44:32 +0100 Subject: [PATCH 7/7] Fix the empty stack detection --- src/coreclr/interpreter/compiler.cpp | 8 ++++++-- src/coreclr/interpreter/compiler.h | 3 +++ 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/src/coreclr/interpreter/compiler.cpp b/src/coreclr/interpreter/compiler.cpp index c07ecae5512161..e68f3adc16f11f 100644 --- a/src/coreclr/interpreter/compiler.cpp +++ b/src/coreclr/interpreter/compiler.cpp @@ -319,12 +319,12 @@ InterpInst* InterpCompiler::NewIns(int opcode, int dataLen) InterpInst *ins = (InterpInst*)getAllocator(IMK_Instruction).allocateZeroed(insSize); ins->opcode = opcode; ins->ilOffset = m_currentILOffset; - ptrdiff_t stackDepth = m_pStackPointer - m_pStackBase; - if ((stackDepth == 0) && ((m_pLastNewIns == nullptr) || (m_pLastNewIns->ilOffset != m_currentILOffset))) + if (m_isFirstInstForEmptyILStack) { // This is the first instruction we are emitting for this IL offset and the stack is empty, which // implies the IL stack is empty too. ins->flags |= INTERP_INST_FLAG_EMPTY_IL_STACK; + m_isFirstInstForEmptyILStack = false; } m_pLastNewIns = ins; return ins; @@ -8386,6 +8386,10 @@ void InterpCompiler::GenerateCode(CORINFO_METHOD_INFO* methodInfo) if (ILOpcodePeeps.FindAndApplyPeep(this)) continue; + + // Empty stack at the beginning of the IL instruction implies IL stack being empty too + m_isFirstInstForEmptyILStack = (m_pStackPointer - m_pStackBase) == 0; + uint8_t opcode = *m_ip; switch (opcode) { diff --git a/src/coreclr/interpreter/compiler.h b/src/coreclr/interpreter/compiler.h index 10ee3d9722e7c3..33ef656ffe5f5a 100644 --- a/src/coreclr/interpreter/compiler.h +++ b/src/coreclr/interpreter/compiler.h @@ -672,6 +672,9 @@ class InterpCompiler int32_t m_currentILOffset; InterpInst* m_pInitLocalsIns; + // Indicates that we are going to generate the first interpreter byte code instruction for an IL opcode with an empty stack. + bool m_isFirstInstForEmptyILStack = true; + // If the method has a hidden argument, GenerateCode allocates a var to store it and // populates the var at method entry int32_t m_hiddenArgumentVar;