Skip to content

Commit 1d02af4

Browse files
tommcdonCopilot
andauthored
Adjust interpreter debugger IL mapping (#127469)
When the debugger walks the stack, interpreter frames sometimes report incorrect source line due to IL -> native mapping differences compared to how the JIT generates mappings. To better align interpreter debug output with how the debugger maps line numbers: 1. The current model generates more mapping IL->native data than we need. Only emit sequence points on IL stack empty points and `nop` instructions. 2. When stackwalking, the return address points to the instruction after the 'call' instruction. The interpreter eliminates certain IL instructions from the input stream, sometimes causing the next IL instruction to be mapped to the next line number. To address this, 1) when the IL code is built in debug mode, and 2) if an IL instruction is eliminated immediately after a call instruction, we will insert a `nop` IL instruction in its place. --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent 0b6bb45 commit 1d02af4

2 files changed

Lines changed: 13 additions & 2 deletions

File tree

src/coreclr/interpreter/compiler.cpp

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1054,7 +1054,9 @@ int32_t* InterpCompiler::EmitCodeIns(int32_t *ip, InterpInst *ins, TArray<Reloc*
10541054
if (ilOffset < (uint32_t)m_ILCodeSizeFromILHeader)
10551055
{
10561056
uint32_t nativeOffset = ConvertOffset(ins->nativeOffset);
1057-
if ((m_ILToNativeMapSize == 0) || (m_pILToNativeMap[m_ILToNativeMapSize - 1].ilOffset != ilOffset))
1057+
// Only emit mapping entries at IL offsets where the evaluation stack is empty
1058+
if ((ins->flags & INTERP_INST_FLAG_EMPTY_IL_STACK) &&
1059+
((m_ILToNativeMapSize == 0) || (m_pILToNativeMap[m_ILToNativeMapSize - 1].ilOffset != ilOffset)))
10581060
{
10591061
// This code assumes that instructions for the same IL offset are emitted in a single run without
10601062
// any other IL offsets in between and that they don't repeat again after the run ends.
@@ -1076,7 +1078,7 @@ int32_t* InterpCompiler::EmitCodeIns(int32_t *ip, InterpInst *ins, TArray<Reloc*
10761078

10771079
m_pILToNativeMap[m_ILToNativeMapSize].ilOffset = ilOffset;
10781080
m_pILToNativeMap[m_ILToNativeMapSize].nativeOffset = nativeOffset;
1079-
m_pILToNativeMap[m_ILToNativeMapSize].source = (ins->flags & INTERP_INST_FLAG_EMPTY_IL_STACK) ? ICorDebugInfo::STACK_EMPTY : ICorDebugInfo::SOURCE_TYPE_INVALID;
1081+
m_pILToNativeMap[m_ILToNativeMapSize].source = ICorDebugInfo::STACK_EMPTY;
10801082
m_ILToNativeMapSize++;
10811083
}
10821084
}
@@ -1120,6 +1122,14 @@ int32_t *InterpCompiler::EmitBBCode(int32_t *ip, InterpBasicBlock *bb, TArray<Re
11201122
if (InterpOpIsEmitNop(ins->opcode))
11211123
{
11221124
ins->nativeOffset = (int32_t)(ip - m_pMethodCode);
1125+
if (m_corJitFlags.IsSet(CORJIT_FLAGS::CORJIT_FLAG_DEBUG_CODE))
1126+
{
1127+
// Emit a debug sequence point so that eliminated IL instructions
1128+
// still occupy a bytecode slot. This ensures return addresses after
1129+
// calls land within the call's native offset range rather than on
1130+
// the next statement boundary.
1131+
*ip++ = INTOP_DEBUG_SEQ_POINT;
1132+
}
11231133
continue;
11241134
}
11251135

src/coreclr/vm/eetwain.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2104,6 +2104,7 @@ static void VirtualUnwindInterpreterCallFrame(TADDR sp, T_CONTEXT *pContext)
21042104
pFrame = pFrame->pParent;
21052105
if (pFrame != NULL)
21062106
{
2107+
// The parent frame's IP points past the call instruction (the resumption point).
21072108
SetIP(pContext, (TADDR)pFrame->ip);
21082109
SetSP(pContext, dac_cast<TADDR>(pFrame));
21092110
SetFP(pContext, (TADDR)pFrame->pStack);

0 commit comments

Comments
 (0)