Skip to content
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
38263bf
JIT: Report managed-return-values as a native var
jakobbotsch May 22, 2026
65e2356
Bump JIT-EE GUID
jakobbotsch May 22, 2026
2fcf4c1
Fix theoretical issue
jakobbotsch May 22, 2026
72afb1a
Merge branch 'main' of github.com:dotnet/runtime into mrv-return-vars
jakobbotsch Jun 2, 2026
66a87fe
Add a blurb about version 22
jakobbotsch Jun 2, 2026
556e026
Bump R2R version
jakobbotsch Jun 2, 2026
bc05e7b
Fix R2R reading for version 20 and 22
jakobbotsch Jun 2, 2026
33b9b8f
Fix build
jakobbotsch Jun 2, 2026
1a96758
Update debugger
jakobbotsch Jun 2, 2026
1fde692
Feedback
jakobbotsch Jun 2, 2026
92d77ae
Run jit-format
jakobbotsch Jun 2, 2026
00078d1
Fix GCC build
jakobbotsch Jun 2, 2026
9e59caa
Fix for NativeAOT
jakobbotsch Jun 2, 2026
aa1fc0b
Copilot nits about whitespace
jakobbotsch Jun 2, 2026
184a95b
Run jit-format
jakobbotsch Jun 2, 2026
8c3655e
Remove a leftover JITDUMP
jakobbotsch Jun 2, 2026
fd2a4e2
Merge branch 'main' of github.com:dotnet/runtime into mrv-return-vars
jakobbotsch Jun 3, 2026
f02ddf0
Feedback
jakobbotsch Jun 3, 2026
4556bbd
Feedback
jakobbotsch Jun 3, 2026
3cc35aa
Fix GCC build
jakobbotsch Jun 3, 2026
02f32e5
Remove dead code
jakobbotsch Jun 4, 2026
3da5d6c
add debugger support
rcj1 Jun 2, 2026
7479835
name
rcj1 Jun 5, 2026
e79d7b8
Merge branch 'main' into mrv-return-vars
jakobbotsch Jun 8, 2026
db70c33
Copilot feedback
jakobbotsch Jun 8, 2026
670c950
Merge branch 'main' into mrv-return-vars
jakobbotsch Jun 10, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
418 changes: 18 additions & 400 deletions src/coreclr/debug/di/module.cpp

Large diffs are not rendered by default.

2 changes: 0 additions & 2 deletions src/coreclr/debug/di/rspriv.h
Original file line number Diff line number Diff line change
Expand Up @@ -5921,8 +5921,6 @@ class CordbNativeCode : public CordbCode,
// Grabs the appropriate signature parser for a methodref, methoddef, methodspec.
HRESULT GetSigParserFromFunction(mdToken mdFunction, mdToken *pClass, SigParser &methodSig, SigParser &genericSig);

int GetCallInstructionLength(BYTE *buffer, ULONG32 len);

//-----------------------------------------------------------
// Data members
//-----------------------------------------------------------
Expand Down
23 changes: 2 additions & 21 deletions src/coreclr/debug/ee/debugger.h
Original file line number Diff line number Diff line change
Expand Up @@ -1502,8 +1502,6 @@ class DebuggerJitInfo
ULONG m_lastIL;
PTR_DebuggerILToNativeMap m_sequenceMap;
unsigned int m_sequenceMapCount;
PTR_DebuggerILToNativeMap m_callsiteMap;
unsigned int m_callsiteMapCount;
bool m_sequenceMapSorted;

PTR_NativeVarInfo m_varNativeInfo;
Expand Down Expand Up @@ -1532,10 +1530,9 @@ class DebuggerJitInfo
" m_addrOfCode: %p\n"
" m_sizeOfCode: 0x%zx\n"
" m_lastIL: 0x%x\n"
" m_sequenceMapCount: %u\n"
" m_callsiteMapCount: %u\n",
" m_sequenceMapCount: %u\n",
this, (m_jitComplete ? "true" : "false"), encState,
m_methodInfo, m_addrOfCode, m_sizeOfCode, m_lastIL, m_sequenceMapCount, m_callsiteMapCount));
m_methodInfo, m_addrOfCode, m_sizeOfCode, m_lastIL, m_sequenceMapCount));
#endif //LOGGING
}

Expand All @@ -1556,22 +1553,6 @@ class DebuggerJitInfo
return m_sequenceMap;
}

unsigned int GetCallsiteMapCount()
{
SUPPORTS_DAC;

LazyInitBounds();
return m_callsiteMapCount;
}

PTR_DebuggerILToNativeMap GetCallSiteMap()
{
SUPPORTS_DAC;

LazyInitBounds();
return m_callsiteMap;
}

PTR_NativeVarInfo GetVarNativeInfo()
{
SUPPORTS_DAC;
Expand Down
15 changes: 3 additions & 12 deletions src/coreclr/debug/ee/functioninfo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -249,8 +249,6 @@ DebuggerJitInfo::DebuggerJitInfo(DebuggerMethodInfo *minfo, NativeCodeVersion na
m_lastIL(0),
m_sequenceMap(NULL),
m_sequenceMapCount(0),
m_callsiteMap(NULL),
m_callsiteMapCount(0),
m_sequenceMapSorted(false),
m_varNativeInfo(NULL), m_varNativeInfoCount(0),
m_fAttemptInit(false)
Expand Down Expand Up @@ -1160,18 +1158,11 @@ void DebuggerJitInfo::SetBoundaries(ULONG32 cMap, ICorDebugInfo::OffsetMapping *

m_sequenceMapSorted = true;

m_callsiteMapCount = m_sequenceMapCount;
while (m_sequenceMapCount > 0 && (m_sequenceMap[m_sequenceMapCount-1].source & call_inst) == call_inst)
m_sequenceMapCount--;

m_callsiteMap = m_sequenceMap + m_sequenceMapCount;
m_callsiteMapCount -= m_sequenceMapCount;

LOG((LF_CORDB, LL_INFO100000, "DJI::sB: this=%p boundary count is %u (%u callsites)\n",
this, m_sequenceMapCount, m_callsiteMapCount));
LOG((LF_CORDB, LL_INFO100000, "DJI::sB: this=%p boundary count is %u\n",
this, m_sequenceMapCount));

#ifdef LOGGING
for (unsigned count = 0; count < m_sequenceMapCount + m_callsiteMapCount; count++)
for (unsigned count = 0; count < m_sequenceMapCount; count++)
{
const DebuggerILToNativeMap& entry = m_sequenceMap[count];
switch (entry.ilOffset)
Expand Down
15 changes: 9 additions & 6 deletions src/coreclr/inc/cordebuginfo.h
Original file line number Diff line number Diff line change
Expand Up @@ -382,15 +382,17 @@ class ICorDebugInfo

enum
{
VARARGS_HND_ILNUM = -1, // Value for the CORINFO_VARARGS_HANDLE varNumber
RETBUF_ILNUM = -2, // Pointer to the return-buffer
TYPECTXT_ILNUM = -3, // ParamTypeArg for CORINFO_GENERICS_CTXT_FROM_PARAMTYPEARG
VARARGS_HND_ILNUM = -1, // Value for the CORINFO_VARARGS_HANDLE varNumber
RETBUF_ILNUM = -2, // Pointer to the return-buffer
TYPECTXT_ILNUM = -3, // ParamTypeArg for CORINFO_GENERICS_CTXT_FROM_PARAMTYPEARG
ASYNC_CONTINUATION_ILNUM = -4, // Async continuation argument
CALL_RETURN_ILNUM = -5, // The return value of a call

UNKNOWN_ILNUM = -6, // Unknown variable
Comment thread
jakobbotsch marked this conversation as resolved.
Outdated

UNKNOWN_ILNUM = -5, // Unknown variable

MAX_ILNUM = -5 // Sentinel value. This should be set to the largest magnitude value in the enum
// so that the compression routines know the enum's range.
MAX_ILNUM = -6 // Sentinel value. This should be set to the largest magnitude value in th enum
// so that the compression routines know the enum's range.
Comment thread
jakobbotsch marked this conversation as resolved.
};

struct ILVarInfo
Expand All @@ -404,6 +406,7 @@ class ICorDebugInfo
{
uint32_t startOffset;
uint32_t endOffset;
uint32_t callReturnValueILOffset;
uint32_t varNumber;
VarLoc loc;
};
Expand Down
10 changes: 5 additions & 5 deletions src/coreclr/inc/jiteeversionguid.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,11 +37,11 @@

#include <minipal/guid.h>

constexpr GUID JITEEVersionIdentifier = { /* 31a04b06-915e-42a0-bbd2-c9c397677ae5 */
0x31a04b06,
0x915e,
0x42a0,
{0xbb, 0xd2, 0xc9, 0xc3, 0x97, 0x67, 0x7a, 0xe5}
constexpr GUID JITEEVersionIdentifier = { /* 59df85b8-c0fd-4e40-aea1-68cb2cd916cc */
0x59df85b8,
0xc0fd,
0x4e40,
{0xae, 0xa1, 0x68, 0xcb, 0x2c, 0xd9, 0x16, 0xcc}
};

#endif // JIT_EE_VERSIONING_GUID_H
5 changes: 3 additions & 2 deletions src/coreclr/inc/readytorun.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,10 @@
// src/coreclr/nativeaot/Runtime/inc/ModuleHeaders.h
// If you update this, ensure you run `git grep MINIMUM_READYTORUN_MAJOR_VERSION`
// and handle pending work.
#define READYTORUN_MAJOR_VERSION 20
#define READYTORUN_MAJOR_VERSION 22
#define READYTORUN_MINOR_VERSION 0x0000

#define MINIMUM_READYTORUN_MAJOR_VERSION 20
#define MINIMUM_READYTORUN_MAJOR_VERSION 22

Comment thread
jakobbotsch marked this conversation as resolved.
Comment thread
jakobbotsch marked this conversation as resolved.
// R2R Version 2.1 adds the InliningInfo section
// R2R Version 2.2 adds the ProfileDataInfo section
Expand Down Expand Up @@ -60,6 +60,7 @@
// R2R Version 18.7 adds READYTORUN_HELPER_R2RToInterpreter
// R2R Version 19 removes the READYTORUN_HELPER_ByRefWriteBarrier helper
// R2R Version 20 changes NativeVarInfo encoding to include ASYNC_CONTINUATION_ILNUM
// R2R Version 22 changes NativeVarInfo encoding to include CALL_RETURN_VALUE

struct READYTORUN_CORE_HEADER
{
Expand Down
12 changes: 1 addition & 11 deletions src/coreclr/jit/codegenarmarch.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3295,17 +3295,7 @@ void CodeGen::genCallInstruction(GenTreeCall* call)

params.isJump = call->IsFastTailCall();
params.hasAsyncRet = call->IsAsync();

// We need to propagate the debug information to the call instruction, so we can emit
// an IL to native mapping record for the call, to support managed return value debugging.
// We don't want tail call helper calls that were converted from normal calls to get a record,
// so we skip this hash table lookup logic in that case.
if (m_compiler->opts.compDbgInfo && m_compiler->genCallSite2DebugInfoMap != nullptr && !call->IsTailCall())
{
DebugInfo di;
(void)m_compiler->genCallSite2DebugInfoMap->Lookup(call, &di);
params.debugInfo = di;
}
params.returnValueCall = call;

#ifdef DEBUG
// Pass the call signature information down into the emitter so the emitter can associate
Expand Down
92 changes: 92 additions & 0 deletions src/coreclr/jit/codegencommon.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1758,6 +1758,98 @@ void CodeGen::genEmitCallWithCurrentGC(EmitCallParams& params)
params.gcrefRegs = gcInfo.gcRegGCrefSetCur;
params.byrefRegs = gcInfo.gcRegByrefSetCur;
GetEmitter()->emitIns_Call(params);

// Emit an entry for managed return value reporting, if needed.
GenTreeCall* call = params.returnValueCall;
JITDUMP("Emit ret val for [%06u]\n", call == nullptr ? 0 : Compiler::dspTreeID(call));
if ((call == nullptr) || !m_compiler->opts.compDbgInfo || (m_compiler->genCallSite2DebugInfoMap == nullptr) || params.isJump)
{
return;
Comment thread
jakobbotsch marked this conversation as resolved.
}
Comment thread
jakobbotsch marked this conversation as resolved.
Comment thread
jakobbotsch marked this conversation as resolved.

if (call->gtReturnType == TYP_VOID)
{
return;
}

DebugInfo di;
if (!m_compiler->genCallSite2DebugInfoMap->Lookup(call, &di))
{
return;
}
Comment thread
jakobbotsch marked this conversation as resolved.

emitLocation retLoc;
retLoc.CaptureLocation(GetEmitter());

CodeGenInterface::EmittedCallReturnInfo info;
info.callILOffset = di.GetRoot().GetLocation().GetOffset();
info.returnLocation = retLoc;

CallArg* retBuf = call->gtArgs.GetRetBufferArg();
if (retBuf != nullptr)
Comment thread
jakobbotsch marked this conversation as resolved.
{
GenTree* node = retBuf->GetNode();
assert(node->OperIsPutArg());

node = node->gtGetOp1()->gtSkipReloadOrCopy();
if (!node->OperIs(GT_LCL_ADDR))
{
return;
}

unsigned lclNum = node->AsLclVarCommon()->GetLclNum();
unsigned lclOffs = node->AsLclVarCommon()->GetLclOffs();
int stackLevelBias = 0;
#ifdef TARGET_X86
stackLevelBias = getCurrentStackLevel();
if (params.argSize > 0)
{
// Call popped these but stack level hasn't been adjusted yet, account for it here
stackLevelBias -= (int)params.argSize;
}
#endif

info.returnValueLoc = getSiVarLoc(m_compiler->lvaGetDesc(lclNum), lclOffs, stackLevelBias);
}
Comment thread
jakobbotsch marked this conversation as resolved.
else if (call->HasMultiRegRetVal())
{
const ReturnTypeDesc* retDesc = call->GetReturnTypeDesc();
unsigned numRegs = retDesc->GetReturnRegCount();
if (numRegs > 2)
{
// Cannot encode more than 2 registers.
return;
}

assert(numRegs == 2);
info.returnValueLoc.storeVariableInRegisters(
retDesc->GetABIReturnReg(0, call->GetUnmanagedCallConv()),
retDesc->GetABIReturnReg(1, call->GetUnmanagedCallConv()));
}
else if (varTypeIsFloating(call))
{
#ifdef TARGET_X86
info.returnValueLoc.vlType = VLT_FPSTK;
info.returnValueLoc.vlFPstk.vlfReg = 0;
#else
info.returnValueLoc.storeVariableInRegisters(REG_FLOATRET, REG_NA);
#endif
}
else if (varTypeUsesFloatReg(call))
{
info.returnValueLoc.storeVariableInRegisters(REG_FLOATRET, REG_NA);
}
else
{
info.returnValueLoc.storeVariableInRegisters(REG_INTRET, REG_NA);
}

if (emittedCallReturnInfo == nullptr)
{
emittedCallReturnInfo = new (m_compiler, CMK_DebugInfo) jitstd::vector<EmittedCallReturnInfo>(m_compiler->getAllocator(CMK_DebugInfo));
}

emittedCallReturnInfo->push_back(info);
}

/*****************************************************************************
Expand Down
11 changes: 10 additions & 1 deletion src/coreclr/jit/codegeninterface.h
Original file line number Diff line number Diff line change
Expand Up @@ -652,8 +652,15 @@ class CodeGenInterface
const LclVarDsc* varDsc, var_types type, regNumber baseReg, int offset, bool isFramePointerUsed);
};

struct EmittedCallReturnInfo
{
IL_OFFSET callILOffset;
emitLocation returnLocation;
siVarLoc returnValueLoc;
};

public:
siVarLoc getSiVarLoc(const LclVarDsc* varDsc, unsigned int stackLevel) const;
siVarLoc getSiVarLoc(const LclVarDsc* varDsc, unsigned offset, unsigned stackLevel) const;
Comment thread
jakobbotsch marked this conversation as resolved.
Outdated

#ifdef DEBUG
void dumpSiVarLoc(const siVarLoc* varLoc) const;
Expand Down Expand Up @@ -864,6 +871,8 @@ class CodeGenInterface
protected:
VariableLiveKeeper* varLiveKeeper; // Used to manage VariableLiveRanges of variables

jitstd::vector<EmittedCallReturnInfo>* emittedCallReturnInfo;

Comment thread
jakobbotsch marked this conversation as resolved.
#ifdef LATE_DISASM
public:
virtual const char* siRegVarName(size_t offs, size_t size, unsigned reg) = 0;
Expand Down
1 change: 1 addition & 0 deletions src/coreclr/jit/codegenlinear.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ void CodeGen::genInitialize()
}

initializeVariableLiveKeeper();
emittedCallReturnInfo = new (m_compiler, CMK_DebugInfo) jitstd::vector<EmittedCallReturnInfo>(m_compiler->getAllocator(CMK_DebugInfo));

Comment thread
jakobbotsch marked this conversation as resolved.
Comment thread
jakobbotsch marked this conversation as resolved.
genPendingCallLabel = nullptr;
Comment thread
jakobbotsch marked this conversation as resolved.

Expand Down
12 changes: 1 addition & 11 deletions src/coreclr/jit/codegenwasm.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2477,17 +2477,7 @@ void CodeGen::genCallInstruction(GenTreeCall* call)
EmitCallParams params;
params.isJump = call->IsFastTailCall();
params.hasAsyncRet = call->IsAsync();

// We need to propagate the debug information to the call instruction, so we can emit
// an IL to native mapping record for the call, to support managed return value debugging.
// We don't want tail call helper calls that were converted from normal calls to get a record,
// so we skip this hash table lookup logic in that case.
if (m_compiler->opts.compDbgInfo && m_compiler->genCallSite2DebugInfoMap != nullptr && !call->IsTailCall())
{
DebugInfo di;
(void)m_compiler->genCallSite2DebugInfoMap->Lookup(call, &di);
params.debugInfo = di;
}
params.returnValueCall = call;

#ifdef DEBUG
// Pass the call signature information down into the emitter so the emitter can associate
Expand Down
13 changes: 1 addition & 12 deletions src/coreclr/jit/codegenxarch.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6018,18 +6018,7 @@ void CodeGen::genCallInstruction(GenTreeCall* call X86_ARG(target_ssize_t stackA

params.isJump = call->IsFastTailCall();
params.hasAsyncRet = call->IsAsync();

// We need to propagate the IL offset information to the call instruction, so we can emit
// an IL to native mapping record for the call, to support managed return value debugging.
// We don't want tail call helper calls that were converted from normal calls to get a record,
// so we skip this hash table lookup logic in that case.

if (m_compiler->opts.compDbgInfo && m_compiler->genCallSite2DebugInfoMap != nullptr && !call->IsTailCall())
{
DebugInfo di;
(void)m_compiler->genCallSite2DebugInfoMap->Lookup(call, &di);
params.debugInfo = di;
}
params.returnValueCall = call;

#ifdef DEBUG
// Pass the call signature information down into the emitter so the emitter can associate
Expand Down
7 changes: 5 additions & 2 deletions src/coreclr/jit/compiler.h
Original file line number Diff line number Diff line change
Expand Up @@ -9563,19 +9563,22 @@ class Compiler

void eeGetVars();

unsigned eeVarsCount;
unsigned eeVarsCount = 0;
unsigned eeVarsCapacity = 0;

struct VarResultInfo
{
UNATIVE_OFFSET startOffset;
UNATIVE_OFFSET endOffset;
uint32_t callReturnValueILOffset;
DWORD varNumber;
CodeGenInterface::siVarLoc loc;
}* eeVars;
void eeSetLVcount(unsigned count);
void eeSetLVinfo(unsigned which,
UNATIVE_OFFSET startOffs,
UNATIVE_OFFSET length,
UNATIVE_OFFSET endOffs,
uint32_t callReturnValILOffs,
unsigned varNum,
const CodeGenInterface::siVarLoc& loc);
void eeSetLVdone();
Expand Down
Loading
Loading