Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 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
5 changes: 4 additions & 1 deletion src/coreclr/inc/cordebuginfo.h
Original file line number Diff line number Diff line change
Expand Up @@ -388,7 +388,9 @@ class ICorDebugInfo

UNKNOWN_ILNUM = -4, // Unknown variable

MAX_ILNUM = -4 // Sentinel value. This should be set to the largest magnitude value in th enum
CALL_RETURN_ILNUM = -5, // The return value of a call

MAX_ILNUM = -5 // Sentinel value. This should be set to the largest magnitude value in th enum
// so that the compression routines know the enum's range.
};

Expand All @@ -403,6 +405,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 = { /* 5635ae9d-ffa5-4336-b027-a383971e3918 */
0x5635ae9d,
0xffa5,
0x4336,
{0xb0, 0x27, 0xa3, 0x83, 0x97, 0x1e, 0x39, 0x18}
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
12 changes: 1 addition & 11 deletions src/coreclr/jit/codegenarmarch.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3255,17 +3255,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 @@ -1759,6 +1759,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 -= 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 @@ -646,8 +646,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 @@ -858,6 +865,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 @@ -2473,17 +2473,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 @@ -6196,18 +6196,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 @@ -9516,19 +9516,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
24 changes: 15 additions & 9 deletions src/coreclr/jit/ee_il_dll.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -670,10 +670,10 @@ void Compiler::eeSetLVcount(unsigned count)

JITDUMP("VarLocInfo count is %d\n", count);

eeVarsCount = count;
if (eeVarsCount)
eeVarsCapacity = count;
if (count > 0)
{
eeVars = (VarResultInfo*)info.compCompHnd->allocateArray(eeVarsCount * sizeof(eeVars[0]));
eeVars = (VarResultInfo*)info.compCompHnd->allocateArray(count * sizeof(eeVars[0]));
Comment thread
jakobbotsch marked this conversation as resolved.
Outdated
}
else
{
Expand All @@ -683,21 +683,22 @@ void Compiler::eeSetLVcount(unsigned count)

void Compiler::eeSetLVinfo(unsigned which,
UNATIVE_OFFSET startOffs,
UNATIVE_OFFSET length,
UNATIVE_OFFSET endOffs,
uint32_t callReturnValueILOffset,
unsigned varNum,
const CodeGenInterface::siVarLoc& varLoc)
{
// ICorDebugInfo::VarLoc and CodeGenInterface::siVarLoc have to overlap
// This is checked in siInit()

assert(opts.compScopeInfo);
assert(eeVarsCount > 0);
assert(which < eeVarsCount);
assert(which < eeVarsCapacity);

if (eeVars != nullptr)
{
eeVars[which].startOffset = startOffs;
eeVars[which].endOffset = startOffs + length;
eeVars[which].endOffset = endOffs;
eeVars[which].callReturnValueILOffset = callReturnValueILOffset;
eeVars[which].varNumber = varNum;
eeVars[which].loc = varLoc;
}
Expand Down Expand Up @@ -870,15 +871,20 @@ void Compiler::eeDispVar(ICorDebugInfo::NativeVarInfo* var)
{
name = "typeCtx";
}
if (0 <= var->varNumber && var->varNumber < lvaCount)

if (var->varNumber == (DWORD)ICorDebugInfo::CALL_RETURN_ILNUM)
{
int printed = printf("(call %03u)", var->callReturnValueILOffset);
}
Comment thread
jakobbotsch marked this conversation as resolved.
Comment thread
jakobbotsch marked this conversation as resolved.
else if (0 <= var->varNumber && var->varNumber < lvaCount)
{
printf("(");
gtDispLclVar(var->varNumber, false);
printf(")");
}
else
{
printf("(%10s)", (VarNameToStr(name) == nullptr) ? "UNKNOWN" : VarNameToStr(name));
printf("(%8s)", (VarNameToStr(name) == nullptr) ? "UNKNOWN" : VarNameToStr(name));
}
printf(" : From %08Xh to %08Xh, in ", var->startOffset, var->endOffset);

Expand Down
3 changes: 2 additions & 1 deletion src/coreclr/jit/emit.h
Original file line number Diff line number Diff line change
Expand Up @@ -482,13 +482,14 @@ struct EmitCallParams
BitVec ptrVars = BitVecOps::UninitVal();
regMaskTP gcrefRegs = RBM_NONE;
regMaskTP byrefRegs = RBM_NONE;
DebugInfo debugInfo;
regNumber ireg = REG_NA;
regNumber xreg = REG_NA;
unsigned xmul = 0;
ssize_t disp = 0;
bool isJump = false;
bool noSafePoint = false;
// If this call should have managed return value debug info associated with it, this is the call to associate it with.
GenTreeCall* returnValueCall = nullptr;
Comment thread
jakobbotsch marked this conversation as resolved.
#ifdef TARGET_WASM
CORINFO_WASM_TYPE_SYMBOL_HANDLE wasmSignature = nullptr;
#endif
Comment thread
jakobbotsch marked this conversation as resolved.
Expand Down
6 changes: 0 additions & 6 deletions src/coreclr/jit/emitarm.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4683,12 +4683,6 @@ void emitter::emitIns_Call(const EmitCallParams& params)
}
#endif

/* Managed RetVal: emit sequence point for the call */
if (m_compiler->opts.compDbgInfo && params.debugInfo.GetLocation().IsValid())
{
codeGen->genIPmappingAdd(IPmappingDscKind::Normal, params.debugInfo, false);
}

/*
We need to allocate the appropriate instruction descriptor based
on whether this is a direct/indirect call, and whether we need to
Expand Down
6 changes: 0 additions & 6 deletions src/coreclr/jit/emitarm64.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9493,12 +9493,6 @@ void emitter::emitIns_Call(const EmitCallParams& params)
}
#endif

/* Managed RetVal: emit sequence point for the call */
if (m_compiler->opts.compDbgInfo && params.debugInfo.GetLocation().IsValid())
{
codeGen->genIPmappingAdd(IPmappingDscKind::Normal, params.debugInfo, false);
}

/*
We need to allocate the appropriate instruction descriptor based
on whether this is a direct/indirect call, and whether we need to
Expand Down
6 changes: 0 additions & 6 deletions src/coreclr/jit/emitloongarch64.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2460,12 +2460,6 @@ void emitter::emitIns_Call(const EmitCallParams& params)
}
#endif

/* Managed RetVal: emit sequence point for the call */
if (m_compiler->opts.compDbgInfo && params.debugInfo.GetLocation().IsValid())
{
codeGen->genIPmappingAdd(IPmappingDscKind::Normal, params.debugInfo, false);
}

/*
We need to allocate the appropriate instruction descriptor based
on whether this is a direct/indirect call, and whether we need to
Expand Down
6 changes: 0 additions & 6 deletions src/coreclr/jit/emitriscv64.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1941,12 +1941,6 @@ void emitter::emitIns_Call(const EmitCallParams& params)
emitLoadImmediate<true>(EA_PTRSIZE, params.ireg, imm); // upper bits
}

/* Managed RetVal: emit sequence point for the call */
if (m_compiler->opts.compDbgInfo && params.debugInfo.GetLocation().IsValid())
{
codeGen->genIPmappingAdd(IPmappingDscKind::Normal, params.debugInfo, false);
}

/*
We need to allocate the appropriate instruction descriptor based
on whether this is a direct/indirect call, and whether we need to
Expand Down
6 changes: 0 additions & 6 deletions src/coreclr/jit/emitwasm.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -179,12 +179,6 @@ void emitter::emitIns_Call(const EmitCallParams& params)
assert(params.callType < EC_COUNT);
assert((params.callType == EC_FUNC_TOKEN) || (params.addr == nullptr));

/* Managed RetVal: emit sequence point for the call */
if (m_compiler->opts.compDbgInfo && params.debugInfo.GetLocation().IsValid())
{
codeGen->genIPmappingAdd(IPmappingDscKind::Normal, params.debugInfo, false);
}

assert(params.wasmSignature != nullptr);

/*
Expand Down
Loading
Loading