diff --git a/docs/design/datacontracts/DebugInfo.md b/docs/design/datacontracts/DebugInfo.md index 529b9db44d30b2..212e2c38e8d84c 100644 --- a/docs/design/datacontracts/DebugInfo.md +++ b/docs/design/datacontracts/DebugInfo.md @@ -343,6 +343,7 @@ public readonly struct DebugVarInfo public int StackOffset { get; init; } public uint BaseRegister2 { get; init; } public int StackOffset2 { get; init; } + public uint CallReturnValueILOffset { get; init; } } // Given a code pointer, return the variable location info for the method. @@ -352,7 +353,8 @@ IEnumerable GetMethodVarInfo(TargetCodePointer pCode, out uint cod Additional constants (Version 2): | Constant Name | Meaning | Value | | --- | --- | --- | -| `MAX_ILNUM` | Bias for adjusted encoding of variable numbers | `0xfffffffc` (-4) | +| `MAX_ILNUM` | Bias for adjusted encoding of variable numbers | `0xfffffffa` (-6) | +| `CALL_RETURN_ILNUM` | Special variable number identifying a call-return-value entry | `0xfffffffb` (-5) | | `VLT_REG` | Variable is in a register | `0` | | `VLT_REG_BYREF` | Address of the variable is in a register | `1` | | `VLT_REG_FP` | Variable is in an FP register | `2` | @@ -371,9 +373,11 @@ Additional constants (Version 2): Each variable entry in the Vars section is nibble-encoded as follows: -1. `startOffset` — encoded unsigned 32-bit integer -2. `endOffset` — encoded as delta from `startOffset` (unsigned) -3. `varNumber` — encoded as adjusted unsigned (`value - MAX_ILNUM`) +1. `varNumber` — encoded as adjusted unsigned (`value - MAX_ILNUM`) +2. `startOffset` — encoded unsigned 32-bit integer +3. The next field depends on `varNumber`: + - If `varNumber == CALL_RETURN_ILNUM`: `callReturnValueILOffset` — encoded unsigned 32-bit integer (IL offset of the call site whose return value this entry describes). `endOffset` is implicit and equals `startOffset + 1`. + - Otherwise: `endOffset` — encoded as delta from `startOffset` (unsigned). `callReturnValueILOffset` is implicit and equals `0`. 4. `VarLocType` — encoded unsigned 32-bit integer 5. Location fields depend on the `VarLocType`: diff --git a/src/coreclr/debug/di/module.cpp b/src/coreclr/debug/di/module.cpp index 5dac6157145ab8..3bd2ea636603b3 100644 --- a/src/coreclr/debug/di/module.cpp +++ b/src/coreclr/debug/di/module.cpp @@ -4237,7 +4237,24 @@ HRESULT CordbNativeCode::GetReturnValueLiveOffset(ULONG32 ILoffset, ULONG32 buff ATT_REQUIRE_STOPPED_MAY_FAIL(GetProcess()); EX_TRY { - hr = GetReturnValueLiveOffsetImpl(NULL, ILoffset, bufferSize, pFetched, pOffsets); + LoadNativeInfo(); + + NewArrayHolder varInfos; + const ICorDebugInfo::NativeVarInfo **pVarInfosBuffer = NULL; + if (pOffsets != NULL && bufferSize > 0) + { + varInfos = new const ICorDebugInfo::NativeVarInfo *[bufferSize]; + pVarInfosBuffer = varInfos; + } + + hr = GetReturnValueVariableHomes(NULL, ILoffset, bufferSize, pFetched, pVarInfosBuffer); + if ((SUCCEEDED(hr) || hr == S_FALSE) && pVarInfosBuffer != NULL) + { + for (ULONG32 i = 0; i < *pFetched; ++i) + { + pOffsets[i] = pVarInfosBuffer[i]->startOffset; + } + } } EX_CATCH_HRESULT(hr); return hr; @@ -4350,385 +4367,6 @@ HRESULT CordbNativeCode::EnumerateVariableHomes(ICorDebugVariableHomeEnum **ppEn return hr; } -int CordbNativeCode::GetCallInstructionLength(BYTE *ip, ULONG32 count) -{ -#if defined(TARGET_ARM) || defined(TARGET_RISCV64) - if (Is32BitInstruction(*(WORD*)ip)) - return 4; - else - return 2; -#elif defined(TARGET_ARM64) - return MAX_INSTRUCTION_LENGTH; -#elif defined(TARGET_LOONGARCH64) - return MAX_INSTRUCTION_LENGTH; -#elif defined(TARGET_X86) - if (count < 2) - return -1; - - // Skip instruction prefixes - do - { - switch (*ip) - { - // Segment overrides - case 0x26: // ES - case 0x2E: // CS - case 0x36: // SS - case 0x3E: // DS - case 0x64: // FS - case 0x65: // GS - - // Size overrides - case 0x66: // Operand-Size - case 0x67: // Address-Size - - // Lock - case 0xf0: - - // String REP prefixes - case 0xf1: - case 0xf2: // REPNE/REPNZ - case 0xf3: - ip++; - count--; - continue; - - default: - break; - } - } while (0); - - // Read the opcode - BYTE opcode = *ip++; - if (opcode == 0xcc) - { - // todo: Can we actually get this result? Doesn't ICorDebug hand out un-patched assembly? - _ASSERTE(!"Hit break opcode!"); - return -1; - } - - // Analyze what we can of the opcode - switch (opcode) - { - case 0xff: - { - // Count may have been decremented by prefixes. - if (count < 2) - return -1; - - BYTE modrm = *ip++; - BYTE mod = (modrm & 0xC0) >> 6; - BYTE reg = (modrm & 0x38) >> 3; - BYTE rm = (modrm & 0x07); - - int displace = -1; - - if ((reg != 2) && (reg != 3) && (reg != 4) && (reg != 5)) - { - // - // This is not a CALL or JMP instruction, return, unknown. - // - _ASSERTE(!"Unhandled opcode!"); - return -1; - } - - - // Only try to decode registers if we actually have reg sets. - switch (mod) - { - case 0: - case 1: - case 2: - - if (rm == 4) - { - if (count < 3) - return -1; - - // - // Get values from the SIB byte - // - BYTE ss = (*ip & 0xC0) >> 6; - BYTE index = (*ip & 0x38) >> 3; - BYTE base = (*ip & 0x7); - - // - // Finally add in the offset - // - if (mod == 0) - { - if (base == 5) - displace = 7; - else - displace = 3; - } - else if (mod == 1) - { - displace = 4; - } - else - { - displace = 7; - } - } - else - { - if (mod == 0) - { - if (rm == 5) - displace = 6; - else - displace = 2; - } - else if (mod == 1) - { - displace = 3; - } - else - { - displace = 6; - } - } - break; - - case 3: - default: - displace = 2; - break; - } - - return displace; - } // end of 0xFF case - - case 0xe8: - return 5; - - - default: - break; - } - - - _ASSERTE(!"Unhandled opcode!"); - return -1; - -#elif defined(TARGET_AMD64) - BYTE rex = 0; - BYTE prefix = *ip; - BOOL fContainsPrefix = FALSE; - - // Should not happen. - if (prefix == 0xcc) - return -1; - - // Skip instruction prefixes - //@TODO by euzem: - //This "loop" can't be really executed more than once so if CALL can really have more than one prefix we'll crash. - //Some of these prefixes are not allowed for CALL instruction and we should treat them as invalid code. - //It appears that this code was mostly copy/pasted from \NDP\clr\src\Debug\EE\amd64\amd64walker.cpp - //with very minimum fixes. - do - { - switch (prefix) - { - // Segment overrides - case 0x26: // ES - case 0x2E: // CS - case 0x36: // SS - case 0x3E: // DS - case 0x64: // FS - case 0x65: // GS - - // Size overrides - case 0x66: // Operand-Size - case 0x67: // Address-Size - - // Lock - case 0xf0: - - // String REP prefixes - case 0xf2: // REPNE/REPNZ - case 0xf3: - ip++; - fContainsPrefix = TRUE; - continue; - - // REX register extension prefixes - case 0x40: - case 0x41: - case 0x42: - case 0x43: - case 0x44: - case 0x45: - case 0x46: - case 0x47: - case 0x48: - case 0x49: - case 0x4a: - case 0x4b: - case 0x4c: - case 0x4d: - case 0x4e: - case 0x4f: - // make sure to set rex to prefix, not *ip because *ip still represents the - // codestream which has a 0xcc in it. - rex = prefix; - ip++; - fContainsPrefix = TRUE; - continue; - - default: - break; - } - } while (0); - - // Read the opcode - BYTE opcode = *ip++; - - // Should not happen. - if (opcode == 0xcc) - return -1; - - - // Setup rex bits if needed - BYTE rex_b = 0; - BYTE rex_x = 0; - BYTE rex_r = 0; - - if (rex != 0) - { - rex_b = (rex & 0x1); // high bit to modrm r/m field or SIB base field or OPCODE reg field -- Hmm, when which? - rex_x = (rex & 0x2) >> 1; // high bit to sib index field - rex_r = (rex & 0x4) >> 2; // high bit to modrm reg field - } - - // Analyze what we can of the opcode - switch (opcode) - { - case 0xff: - { - BYTE modrm = *ip++; - - _ASSERT(modrm != 0); - - BYTE mod = (modrm & 0xC0) >> 6; - BYTE reg = (modrm & 0x38) >> 3; - BYTE rm = (modrm & 0x07); - - reg |= (rex_r << 3); - rm |= (rex_b << 3); - - if ((reg < 2) || (reg > 5 && reg < 8) || (reg > 15)) { - // not a valid register for a CALL or BRANCH - _ASSERTE(!"Invalid opcode!"); - return -1; - } - - SHORT displace = -1; - - // See: Tables A-15,16,17 in AMD Dev Manual 3 for information - // about how the ModRM/SIB/REX bytes interact. - - switch (mod) - { - case 0: - case 1: - case 2: - if ((rm & 0x07) == 4) // we have an SIB byte following - { - // - // Get values from the SIB byte - // - BYTE sib = *ip; - _ASSERT(sib != 0); - - BYTE base = (sib & 0x07); - base |= (rex_b << 3); - - ip++; - - // - // Finally add in the offset - // - if (mod == 0) - { - if ((base & 0x07) == 5) - displace = 7; - else - displace = 3; - } - else if (mod == 1) - { - displace = 4; - } - else // mod == 2 - { - displace = 7; - } - } - else - { - // - // Get the value we need from the register. - // - - // Check for RIP-relative addressing mode. - if ((mod == 0) && ((rm & 0x07) == 5)) - { - displace = 6; // 1 byte opcode + 1 byte modrm + 4 byte displacement (signed) - } - else - { - if (mod == 0) - displace = 2; - else if (mod == 1) - displace = 3; - else // mod == 2 - displace = 6; - } - } - - break; - - case 3: - default: - displace = 2; - } - - // Displace should be set by one of the cases above - if (displace == -1) - { - _ASSERTE(!"GetCallInstructionLength() encountered unexpected call instruction"); - return -1; - } - - // Account for the 1 byte prefix (REX or otherwise) - if (fContainsPrefix) - displace++; - - // reg == 4 or 5 means that it is not a CALL, but JMP instruction - // so we will fall back to ASSERT after break - if ((reg != 4) && (reg != 5)) - return displace; - break; - } - case 0xe8: - { - //Near call with the target specified by a 32-bit relative displacement. - //[maybe 1 byte prefix] + [1 byte opcode E8h] + [4 bytes offset] - return 5 + (fContainsPrefix ? 1 : 0); - } - default: - break; - } - - _ASSERTE(!"Invalid opcode!"); - return -1; -#else -#error Platform not implemented -#endif -} - HRESULT CordbNativeCode::GetSigParserFromFunction(mdToken mdFunction, mdToken *pClass, SigParser &parser, SigParser &methodGenerics) { // mdFunction may be a MemberRef, a MethodDef, or a MethodSpec. We must handle all three cases. @@ -4990,7 +4628,7 @@ HRESULT CordbNativeCode::GetCallSignature(ULONG32 ILoffset, mdToken *pClass, mdT return GetSigParserFromFunction(mdFunction, pClass, parser, generics); } -HRESULT CordbNativeCode::GetReturnValueLiveOffsetImpl(Instantiation *currentInstantiation, ULONG32 ILoffset, ULONG32 bufferSize, ULONG32 *pFetched, ULONG32 *pOffsets) +HRESULT CordbNativeCode::GetReturnValueVariableHomes(Instantiation *currentInstantiation, ULONG32 ILoffset, ULONG32 bufferSize, ULONG32 *pFetched, const ICorDebugInfo::NativeVarInfo **ppVarInfos) { if (pFetched == NULL) return E_INVALIDARG; @@ -5004,36 +4642,33 @@ HRESULT CordbNativeCode::GetReturnValueLiveOffsetImpl(Instantiation *currentInst IfFailRet(GetCallSignature(ILoffset, &mdClass, NULL, signature, generics)); IfFailRet(EnsureReturnValueAllowed(currentInstantiation, mdClass, signature, generics)); - // now find the native offset - SequencePoints *pSP = GetSequencePoints(); - DebuggerILToNativeMap *pMap = pSP->GetCallsiteMapAddr(); - - for (ULONG32 i = 0; i < pSP->GetCallsiteEntryCount() && pMap; ++i, pMap++) + // now find the matching CALL_RETURN_ILNUM NativeVarInfo entries + const DacDbiArrayList *pOffsetInfoList = m_nativeVarData.GetOffsetInfoList(); + _ASSERTE(pOffsetInfoList != NULL); + for (unsigned int i = 0; i < pOffsetInfoList->Count(); i++) { - if (pMap->ilOffset == ILoffset && (pMap->source & ICorDebugInfo::CALL_INSTRUCTION) == ICorDebugInfo::CALL_INSTRUCTION) + const ICorDebugInfo::NativeVarInfo *pNativeVarInfo = &((*pOffsetInfoList)[i]); + _ASSERTE(pNativeVarInfo != NULL); + if (pNativeVarInfo->varNumber != (unsigned)ICorDebugInfo::CALL_RETURN_ILNUM) { - // if we have a buffer, fill it in. - if (pOffsets && found < bufferSize) - { - // Fetch the actual assembly instructions - BYTE nativeBuffer[8]; - - ULONG32 fetched = 0; - IfFailRet(GetCode(pMap->nativeStartOffset, pMap->nativeStartOffset+ARRAY_SIZE(nativeBuffer), ARRAY_SIZE(nativeBuffer), nativeBuffer, &fetched)); - - // Get the length of the call instruction. - int offset = GetCallInstructionLength(nativeBuffer, fetched); - if (offset == -1) - return E_UNEXPECTED; // Could not decode instruction, this should never happen. + continue; + } - pOffsets[found] = pMap->nativeStartOffset + offset; - } + if (pNativeVarInfo->callReturnValueILOffset != ILoffset) + { + continue; + } - found++; + // if we have a buffer, fill it in. + if (ppVarInfos && found < bufferSize) + { + ppVarInfos[found] = pNativeVarInfo; } + + found++; } - if (pOffsets) + if (ppVarInfos) *pFetched = found < bufferSize ? found : bufferSize; else *pFetched = found; @@ -5041,7 +4676,7 @@ HRESULT CordbNativeCode::GetReturnValueLiveOffsetImpl(Instantiation *currentInst if (found == 0) return E_FAIL; - if (pOffsets && found > bufferSize) + if (ppVarInfos && found > bufferSize) return S_FALSE; return S_OK; diff --git a/src/coreclr/debug/di/rspriv.h b/src/coreclr/debug/di/rspriv.h index c61d532dde6680..40de828e71cec3 100644 --- a/src/coreclr/debug/di/rspriv.h +++ b/src/coreclr/debug/di/rspriv.h @@ -5850,7 +5850,7 @@ class CordbNativeCode : public CordbCode, VMPTR_MethodDesc GetVMNativeCodeMethodDescToken() { return m_vmNativeCodeMethodDescToken; }; // Worker function for GetReturnValueLiveOffset. - HRESULT GetReturnValueLiveOffsetImpl(Instantiation *currentInstantiation, ULONG32 ILoffset, ULONG32 bufferSize, ULONG32 *pFetched, ULONG32 *pOffsets); + HRESULT GetReturnValueVariableHomes(Instantiation *currentInstantiation, ULONG32 ILoffset, ULONG32 bufferSize, ULONG32 *pFetched, const ICorDebugInfo::NativeVarInfo **ppVarInfos); // get total size of the code including both hot and cold regions ULONG32 GetSize(); @@ -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 //----------------------------------------------------------- @@ -7324,9 +7322,6 @@ class CordbJITILFrame : public CordbBase, public ICorDebugILFrame, public ICorDe // Worker function for GetReturnValueForILOffset. HRESULT GetReturnValueForILOffsetImpl(ULONG32 ILoffset, ICorDebugValue** ppReturnValue); - // Given pType, fills ppReturnValue with the correct value. - HRESULT GetReturnValueForType(CordbType *pType, ICorDebugValue **ppReturnValue); - //----------------------------------------------------------- // Data members //----------------------------------------------------------- diff --git a/src/coreclr/debug/di/rsthread.cpp b/src/coreclr/debug/di/rsthread.cpp index fc8dbb8e0706e6..4c1de068775829 100644 --- a/src/coreclr/debug/di/rsthread.cpp +++ b/src/coreclr/debug/di/rsthread.cpp @@ -8812,23 +8812,23 @@ HRESULT CordbJITILFrame::GetReturnValueForILOffsetImpl(ULONG32 ILoffset, ICorDeb pCode->LoadNativeInfo(); ULONG32 count = 0; - IfFailRet(pCode->GetReturnValueLiveOffsetImpl(&m_genericArgs, ILoffset, 0, &count, NULL)); + IfFailRet(pCode->GetReturnValueVariableHomes(&m_genericArgs, ILoffset, 0, &count, NULL)); - NewArrayHolder offsets(new ULONG32[count]); - IfFailRet(pCode->GetReturnValueLiveOffsetImpl(&m_genericArgs, ILoffset, count, &count, offsets)); + NewArrayHolder varInfos(new const ICorDebugInfo::NativeVarInfo *[count]); + IfFailRet(pCode->GetReturnValueVariableHomes(&m_genericArgs, ILoffset, count, &count, varInfos)); - bool found = false; + const ICorDebugInfo::NativeVarInfo *pReturnVarInfo = NULL; ULONG32 currentOffset = m_nativeFrame->GetIPOffset(); for (ULONG32 i = 0; i < count; ++i) { - if (currentOffset == offsets[i]) + if (currentOffset == varInfos[i]->startOffset) { - found = true; + pReturnVarInfo = varInfos[i]; break; } } - if (!found) + if (pReturnVarInfo == NULL) return E_UNEXPECTED; // Get the signatures and mdToken for the callee. @@ -8837,69 +8837,13 @@ HRESULT CordbJITILFrame::GetReturnValueForILOffsetImpl(ULONG32 ILoffset, ICorDeb IfFailRet(pCode->GetCallSignature(ILoffset, &targetClass, &mdFunction, methodSig, genericSig)); IfFailRet(CordbNativeCode::SkipToReturn(methodSig)); - - - // Create the Instantiation, type and then return value NewArrayHolder types; Instantiation inst; CordbType *pType = 0; IfFailRet(BuildInstantiationForCallsite(GetModule(), types, inst, &m_genericArgs, targetClass, genericSig)); IfFailRet(CordbType::SigToType(GetModule(), &methodSig, &inst, &pType)); - return GetReturnValueForType(pType, ppReturnValue); -} - - -HRESULT CordbJITILFrame::GetReturnValueForType(CordbType *pType, ICorDebugValue **ppReturnValue) -{ - - -#if defined(TARGET_X86) - const CorDebugRegister floatRegister = REGISTER_X86_FPSTACK_0; -#elif defined(TARGET_AMD64) - const CorDebugRegister floatRegister = REGISTER_AMD64_XMM0; -#elif defined(TARGET_ARM64) - const CorDebugRegister floatRegister = REGISTER_ARM64_V0; -#elif defined(TARGET_ARM) - const CorDebugRegister floatRegister = REGISTER_ARM_D0; -#elif defined(TARGET_LOONGARCH64) - const CorDebugRegister floatRegister = REGISTER_LOONGARCH64_F0; -#elif defined(TARGET_RISCV64) - const CorDebugRegister floatRegister = REGISTER_RISCV64_F0; -#endif - -#if defined(TARGET_X86) - const CorDebugRegister ptrRegister = REGISTER_X86_EAX; - const CorDebugRegister ptrHighWordRegister = REGISTER_X86_EDX; -#elif defined(TARGET_AMD64) - const CorDebugRegister ptrRegister = REGISTER_AMD64_RAX; -#elif defined(TARGET_ARM64) - const CorDebugRegister ptrRegister = REGISTER_ARM64_X0; -#elif defined(TARGET_ARM) - const CorDebugRegister ptrRegister = REGISTER_ARM_R0; - const CorDebugRegister ptrHighWordRegister = REGISTER_ARM_R1; -#elif defined(TARGET_LOONGARCH64) - const CorDebugRegister ptrRegister = REGISTER_LOONGARCH64_A0; -#elif defined(TARGET_RISCV64) - const CorDebugRegister ptrRegister = REGISTER_RISCV64_A0; -#endif - - CorElementType corReturnType = pType->GetElementType(); - switch (corReturnType) - { - default: - return m_nativeFrame->GetLocalRegisterValue(ptrRegister, pType, ppReturnValue); - - case ELEMENT_TYPE_R4: - case ELEMENT_TYPE_R8: - return m_nativeFrame->GetLocalFloatingPointValue(floatRegister, pType, ppReturnValue); - -#if defined(TARGET_X86) || defined(TARGET_ARM) - case ELEMENT_TYPE_I8: - case ELEMENT_TYPE_U8: - return m_nativeFrame->GetLocalDoubleRegisterValue(ptrHighWordRegister, ptrRegister, pType, ppReturnValue); -#endif - } + return GetNativeVariable(pType, pReturnVarInfo, ppReturnValue); } HRESULT CordbJITILFrame::EnumerateLocalVariablesEx(ILCodeKind flags, ICorDebugValueEnum **ppValueEnum) diff --git a/src/coreclr/debug/ee/debugger.h b/src/coreclr/debug/ee/debugger.h index 32df8dbca015f4..3969a776b6c4d3 100644 --- a/src/coreclr/debug/ee/debugger.h +++ b/src/coreclr/debug/ee/debugger.h @@ -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; @@ -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 } @@ -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; diff --git a/src/coreclr/debug/ee/functioninfo.cpp b/src/coreclr/debug/ee/functioninfo.cpp index 3ee9d46810e911..64c62a855b2ae3 100644 --- a/src/coreclr/debug/ee/functioninfo.cpp +++ b/src/coreclr/debug/ee/functioninfo.cpp @@ -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) @@ -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) diff --git a/src/coreclr/inc/cordebuginfo.h b/src/coreclr/inc/cordebuginfo.h index c37fc845cd77e5..ba5a545e3ed909 100644 --- a/src/coreclr/inc/cordebuginfo.h +++ b/src/coreclr/inc/cordebuginfo.h @@ -382,15 +382,16 @@ 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 = -5, // Unknown variable + UNKNOWN_ILNUM = -6, // 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 the enum + // so that the compression routines know the enum's range. }; struct ILVarInfo @@ -404,6 +405,7 @@ class ICorDebugInfo { uint32_t startOffset; uint32_t endOffset; + uint32_t callReturnValueILOffset; uint32_t varNumber; VarLoc loc; }; diff --git a/src/coreclr/inc/jiteeversionguid.h b/src/coreclr/inc/jiteeversionguid.h index 3f1d8e566f18f4..85d8a8682bf489 100644 --- a/src/coreclr/inc/jiteeversionguid.h +++ b/src/coreclr/inc/jiteeversionguid.h @@ -37,11 +37,11 @@ #include -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 diff --git a/src/coreclr/inc/readytorun.h b/src/coreclr/inc/readytorun.h index 83226084f2e40f..3151e5999d7722 100644 --- a/src/coreclr/inc/readytorun.h +++ b/src/coreclr/inc/readytorun.h @@ -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 21 +#define READYTORUN_MAJOR_VERSION 22 #define READYTORUN_MINOR_VERSION 0x0000 -#define MINIMUM_READYTORUN_MAJOR_VERSION 21 +#define MINIMUM_READYTORUN_MAJOR_VERSION 22 // R2R Version 2.1 adds the InliningInfo section // R2R Version 2.2 adds the ProfileDataInfo section @@ -61,6 +61,7 @@ // R2R Version 19 removes the READYTORUN_HELPER_ByRefWriteBarrier helper // R2R Version 20 changes NativeVarInfo encoding to include ASYNC_CONTINUATION_ILNUM // R2R Version 21 updates GC info version to 5 which adds isAsync to x86 GC info +// R2R Version 22 changes NativeVarInfo encoding to include CALL_RETURN_VALUE struct READYTORUN_CORE_HEADER { diff --git a/src/coreclr/jit/codegenarmarch.cpp b/src/coreclr/jit/codegenarmarch.cpp index 2da85c766c5b9a..160dbe08461603 100644 --- a/src/coreclr/jit/codegenarmarch.cpp +++ b/src/coreclr/jit/codegenarmarch.cpp @@ -3293,19 +3293,9 @@ void CodeGen::genCallInstruction(GenTreeCall* call) assert(params.secondRetSize != EA_BYREF); #endif - 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.isJump = call->IsFastTailCall(); + params.hasAsyncRet = call->IsAsync(); + params.returnValueCall = call; #ifdef DEBUG // Pass the call signature information down into the emitter so the emitter can associate diff --git a/src/coreclr/jit/codegencommon.cpp b/src/coreclr/jit/codegencommon.cpp index 95b2df06924de8..f3e4fe5db1c25a 100644 --- a/src/coreclr/jit/codegencommon.cpp +++ b/src/coreclr/jit/codegencommon.cpp @@ -1758,6 +1758,92 @@ 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; + if ((call == nullptr) || !m_compiler->opts.compDbgInfo || (m_compiler->genCallSite2DebugInfoMap == nullptr) || + params.isJump) + { + return; + } + + if (call->gtReturnType == TYP_VOID) + { + return; + } + + DebugInfo di; + if (!m_compiler->genCallSite2DebugInfoMap->Lookup(call, &di)) + { + return; + } + + 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) + { + GenTree* node = retBuf->GetNode(); + assert(node->OperIsPutArg()); + + node = node->gtGetOp1()->gtSkipReloadOrCopy(); + if (!node->OperIs(GT_LCL_ADDR)) + { + return; + } + + unsigned lclNum = node->AsLclVarCommon()->GetLclNum(); + int 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); + } + 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); + } + + emittedCallReturnInfo->push_back(info); } /***************************************************************************** diff --git a/src/coreclr/jit/codegeninterface.h b/src/coreclr/jit/codegeninterface.h index e696c886cd9e28..fac4b90855a287 100644 --- a/src/coreclr/jit/codegeninterface.h +++ b/src/coreclr/jit/codegeninterface.h @@ -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, int offset, int stackLevel) const; #ifdef DEBUG void dumpSiVarLoc(const siVarLoc* varLoc) const; @@ -864,6 +871,8 @@ class CodeGenInterface protected: VariableLiveKeeper* varLiveKeeper; // Used to manage VariableLiveRanges of variables + jitstd::vector* emittedCallReturnInfo; + #ifdef LATE_DISASM public: virtual const char* siRegVarName(size_t offs, size_t size, unsigned reg) = 0; diff --git a/src/coreclr/jit/codegenlinear.cpp b/src/coreclr/jit/codegenlinear.cpp index 00b5ccf2740ac2..85635d64ab5dbe 100644 --- a/src/coreclr/jit/codegenlinear.cpp +++ b/src/coreclr/jit/codegenlinear.cpp @@ -91,6 +91,8 @@ void CodeGen::genInitialize() } initializeVariableLiveKeeper(); + emittedCallReturnInfo = + new (m_compiler, CMK_DebugInfo) jitstd::vector(m_compiler->getAllocator(CMK_DebugInfo)); genPendingCallLabel = nullptr; diff --git a/src/coreclr/jit/codegenloongarch64.cpp b/src/coreclr/jit/codegenloongarch64.cpp index cebfdb45829f35..835936cb2114f7 100644 --- a/src/coreclr/jit/codegenloongarch64.cpp +++ b/src/coreclr/jit/codegenloongarch64.cpp @@ -5583,19 +5583,9 @@ 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.isJump = call->IsFastTailCall(); + params.hasAsyncRet = call->IsAsync(); + params.returnValueCall = call; #ifdef DEBUG // Pass the call signature information down into the emitter so the emitter can associate diff --git a/src/coreclr/jit/codegenriscv64.cpp b/src/coreclr/jit/codegenriscv64.cpp index b91240d58f3ebb..d0b55b10d9f4e0 100644 --- a/src/coreclr/jit/codegenriscv64.cpp +++ b/src/coreclr/jit/codegenriscv64.cpp @@ -5394,19 +5394,9 @@ 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.isJump = call->IsFastTailCall(); + params.hasAsyncRet = call->IsAsync(); + params.returnValueCall = call; #ifdef DEBUG // Pass the call signature information down into the emitter so the emitter can associate diff --git a/src/coreclr/jit/codegenwasm.cpp b/src/coreclr/jit/codegenwasm.cpp index 0bf6b32b214fa5..1e39b8825c8178 100644 --- a/src/coreclr/jit/codegenwasm.cpp +++ b/src/coreclr/jit/codegenwasm.cpp @@ -2522,19 +2522,9 @@ void CodeGen::genCallInstruction(GenTreeCall* call) ensureCurrentFuncIsUnwindable(); 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.isJump = call->IsFastTailCall(); + params.hasAsyncRet = call->IsAsync(); + params.returnValueCall = call; #ifdef DEBUG // Pass the call signature information down into the emitter so the emitter can associate diff --git a/src/coreclr/jit/codegenxarch.cpp b/src/coreclr/jit/codegenxarch.cpp index a8b2e20e24a3f7..3efd0716dfb92f 100644 --- a/src/coreclr/jit/codegenxarch.cpp +++ b/src/coreclr/jit/codegenxarch.cpp @@ -6016,20 +6016,9 @@ 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.isJump = call->IsFastTailCall(); + params.hasAsyncRet = call->IsAsync(); + params.returnValueCall = call; #ifdef DEBUG // Pass the call signature information down into the emitter so the emitter can associate diff --git a/src/coreclr/jit/compiler.h b/src/coreclr/jit/compiler.h index b25ce125243fa1..1e32abbda06f35 100644 --- a/src/coreclr/jit/compiler.h +++ b/src/coreclr/jit/compiler.h @@ -9546,19 +9546,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 eeAllocateLVs(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(); diff --git a/src/coreclr/jit/ee_il_dll.cpp b/src/coreclr/jit/ee_il_dll.cpp index b8835143440fbc..6c3ca0e2a35bba 100644 --- a/src/coreclr/jit/ee_il_dll.cpp +++ b/src/coreclr/jit/ee_il_dll.cpp @@ -664,16 +664,18 @@ void Compiler::eeGetStmtOffsets() * Debugging support - Local var info */ -void Compiler::eeSetLVcount(unsigned count) +void Compiler::eeAllocateLVs(unsigned count) { assert(opts.compScopeInfo); - JITDUMP("VarLocInfo count is %d\n", count); + JITDUMP("Allocating %d VarLocInfo\n", count); - eeVarsCount = count; - if (eeVarsCount) + eeVarsCount = 0; + eeVarsCapacity = count; + + if (count > 0) { - eeVars = (VarResultInfo*)info.compCompHnd->allocateArray(eeVarsCount * sizeof(eeVars[0])); + eeVars = (VarResultInfo*)info.compCompHnd->allocateArray(count * sizeof(eeVars[0])); } else { @@ -683,7 +685,8 @@ 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) { @@ -691,15 +694,15 @@ void Compiler::eeSetLVinfo(unsigned which, // 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].varNumber = varNum; - eeVars[which].loc = varLoc; + eeVars[which].startOffset = startOffs; + eeVars[which].endOffset = endOffs; + eeVars[which].callReturnValueILOffset = callReturnValueILOffset; + eeVars[which].varNumber = varNum; + eeVars[which].loc = varLoc; } } @@ -870,7 +873,12 @@ void Compiler::eeDispVar(ICorDebugInfo::NativeVarInfo* var) { name = "typeCtx"; } - if (0 <= var->varNumber && var->varNumber < lvaCount) + + if (var->varNumber == (DWORD)ICorDebugInfo::CALL_RETURN_ILNUM) + { + printf("(call %03u)", var->callReturnValueILOffset); + } + else if (0 <= var->varNumber && var->varNumber < lvaCount) { printf("("); gtDispLclVar(var->varNumber, false); @@ -878,7 +886,7 @@ void Compiler::eeDispVar(ICorDebugInfo::NativeVarInfo* var) } 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); diff --git a/src/coreclr/jit/emit.h b/src/coreclr/jit/emit.h index 014d5f962e3013..64306e35cd8a24 100644 --- a/src/coreclr/jit/emit.h +++ b/src/coreclr/jit/emit.h @@ -482,13 +482,15 @@ 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; + 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; #ifdef TARGET_WASM CORINFO_WASM_TYPE_SYMBOL_HANDLE wasmSignature = nullptr; #endif diff --git a/src/coreclr/jit/emitarm.cpp b/src/coreclr/jit/emitarm.cpp index f0696f010d3331..60fb4d3558d4bf 100644 --- a/src/coreclr/jit/emitarm.cpp +++ b/src/coreclr/jit/emitarm.cpp @@ -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 diff --git a/src/coreclr/jit/emitarm64.cpp b/src/coreclr/jit/emitarm64.cpp index b781e66a33d433..71b03f23c7d7c3 100644 --- a/src/coreclr/jit/emitarm64.cpp +++ b/src/coreclr/jit/emitarm64.cpp @@ -9486,12 +9486,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 diff --git a/src/coreclr/jit/emitloongarch64.cpp b/src/coreclr/jit/emitloongarch64.cpp index 716e15f392627f..dedb4aaf034795 100644 --- a/src/coreclr/jit/emitloongarch64.cpp +++ b/src/coreclr/jit/emitloongarch64.cpp @@ -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 diff --git a/src/coreclr/jit/emitriscv64.cpp b/src/coreclr/jit/emitriscv64.cpp index f1de046359846e..6ecd43185f330b 100644 --- a/src/coreclr/jit/emitriscv64.cpp +++ b/src/coreclr/jit/emitriscv64.cpp @@ -1941,12 +1941,6 @@ void emitter::emitIns_Call(const EmitCallParams& params) emitLoadImmediate(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 diff --git a/src/coreclr/jit/emitwasm.cpp b/src/coreclr/jit/emitwasm.cpp index 0c89b3d491bdf2..7fc6fa32b742f9 100644 --- a/src/coreclr/jit/emitwasm.cpp +++ b/src/coreclr/jit/emitwasm.cpp @@ -217,12 +217,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); /* diff --git a/src/coreclr/jit/emitxarch.cpp b/src/coreclr/jit/emitxarch.cpp index eadf46bb988a87..68251724a5534d 100644 --- a/src/coreclr/jit/emitxarch.cpp +++ b/src/coreclr/jit/emitxarch.cpp @@ -11374,12 +11374,6 @@ void emitter::emitIns_Call(const EmitCallParams& params) } #endif - /* Managed RetVal: emit sequence point for the call */ - if (m_compiler->opts.compDbgInfo && params.debugInfo.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 diff --git a/src/coreclr/jit/scopeinfo.cpp b/src/coreclr/jit/scopeinfo.cpp index 9fc32e0fb48798..e5dd272c33b9a4 100644 --- a/src/coreclr/jit/scopeinfo.cpp +++ b/src/coreclr/jit/scopeinfo.cpp @@ -486,6 +486,7 @@ CodeGenInterface::siVarLoc::siVarLoc(const LclVarDsc* varDsc, regNumber baseReg, // // Arguments: // varDsc - the variable it is desired to build the "siVarLoc". +// offset - offset into the variable to add // stackLevel - the current stack level. If the stack pointer changes in // the function, we must adjust stack pointer-based local // variable offsets to compensate. @@ -494,12 +495,12 @@ CodeGenInterface::siVarLoc::siVarLoc(const LclVarDsc* varDsc, regNumber baseReg, // A "siVarLoc" representing the variable location, which could live // in a register, an stack position, or a combination of both. // -CodeGenInterface::siVarLoc CodeGenInterface::getSiVarLoc(const LclVarDsc* varDsc, unsigned int stackLevel) const +CodeGenInterface::siVarLoc CodeGenInterface::getSiVarLoc(const LclVarDsc* varDsc, int offset, int stackLevel) const { // For stack vars, find the base register, and offset regNumber baseReg; - signed offset = varDsc->GetStackOffset(); + offset += varDsc->GetStackOffset(); if (!varDsc->lvFramePointerBased) { @@ -1080,7 +1081,7 @@ void CodeGenInterface::VariableLiveKeeper::siStartVariableLiveRange(const LclVar { // Build siVarLoc for this born "varDsc" CodeGenInterface::siVarLoc varLocation = - m_compiler->codeGen->getSiVarLoc(varDsc, m_compiler->codeGen->getCurrentStackLevel()); + m_compiler->codeGen->getSiVarLoc(varDsc, 0, (int)m_compiler->codeGen->getCurrentStackLevel()); VariableLiveDescriptor* varLiveDsc = &m_vlrLiveDsc[varNum]; // this variable live range is valid from this point @@ -1149,7 +1150,7 @@ void CodeGenInterface::VariableLiveKeeper::siUpdateVariableLiveRange(const LclVa { // Build the location of the variable CodeGenInterface::siVarLoc siVarLoc = - m_compiler->codeGen->getSiVarLoc(varDsc, m_compiler->codeGen->getCurrentStackLevel()); + m_compiler->codeGen->getSiVarLoc(varDsc, 0, (int)m_compiler->codeGen->getCurrentStackLevel()); // Report the home change for this variable VariableLiveDescriptor* varLiveDsc = &m_vlrLiveDsc[varNum]; @@ -1781,22 +1782,21 @@ void CodeGen::genSetScopeInfo() } #endif - unsigned varsLocationsCount = 0; - - varsLocationsCount = (unsigned int)varLiveKeeper->getLiveRangesCount(); + unsigned varsLocationsCount = (unsigned int)(varLiveKeeper->getLiveRangesCount() + emittedCallReturnInfo->size()); if (varsLocationsCount == 0) { // No variable home to report - m_compiler->eeSetLVcount(0); + m_compiler->eeAllocateLVs(0); m_compiler->eeSetLVdone(); return; } - noway_assert(m_compiler->opts.compScopeInfo && (m_compiler->info.compVarScopesCount > 0)); + noway_assert((m_compiler->opts.compScopeInfo && (m_compiler->info.compVarScopesCount > 0)) || + (varLiveKeeper->getLiveRangesCount() == 0)); // Initialize the table where the reported variables' home will be placed. - m_compiler->eeSetLVcount(varsLocationsCount); + m_compiler->eeAllocateLVs(varsLocationsCount); #ifdef DEBUG genTrnslLocalVarCount = varsLocationsCount; @@ -1806,11 +1806,18 @@ void CodeGen::genSetScopeInfo() } #endif - // We can have one of both flags defined, both, or none. Specially if we need to compare both - // both results. But we cannot report both to the debugger, since there would be overlapping - // intervals, and may not indicate the same variable location. + if (varLiveKeeper->getLiveRangesCount() > 0) + { + genSetScopeInfoUsingVariableRanges(); + } + + for (const EmittedCallReturnInfo& callReturnInfo : *emittedCallReturnInfo) + { + UNATIVE_OFFSET retOffset = callReturnInfo.returnLocation.CodeOffset(GetEmitter()); - genSetScopeInfoUsingVariableRanges(); + m_compiler->eeSetLVinfo(m_compiler->eeVarsCount++, retOffset, retOffset + 1, callReturnInfo.callILOffset, + ICorDebugInfo::CALL_RETURN_ILNUM, callReturnInfo.returnValueLoc); + } m_compiler->eeSetLVdone(); } @@ -2001,7 +2008,7 @@ void CodeGen::genSetScopeInfo(unsigned which, #endif // DEBUG - m_compiler->eeSetLVinfo(which, startOffs, length, ilVarNum, *varLoc); + m_compiler->eeSetLVinfo(which, startOffs, startOffs + length, 0, ilVarNum, *varLoc); } /*****************************************************************************/ diff --git a/src/coreclr/nativeaot/Runtime/inc/ModuleHeaders.h b/src/coreclr/nativeaot/Runtime/inc/ModuleHeaders.h index aec10391749808..0777cfeb07b38c 100644 --- a/src/coreclr/nativeaot/Runtime/inc/ModuleHeaders.h +++ b/src/coreclr/nativeaot/Runtime/inc/ModuleHeaders.h @@ -11,7 +11,7 @@ struct ReadyToRunHeaderConstants { static const uint32_t Signature = 0x00525452; // 'RTR' - static const uint32_t CurrentMajorVersion = 21; + static const uint32_t CurrentMajorVersion = 22; static const uint32_t CurrentMinorVersion = 0; }; diff --git a/src/coreclr/tools/Common/Internal/Runtime/ModuleHeaders.cs b/src/coreclr/tools/Common/Internal/Runtime/ModuleHeaders.cs index cdbeaa85f35552..00d43786d72f75 100644 --- a/src/coreclr/tools/Common/Internal/Runtime/ModuleHeaders.cs +++ b/src/coreclr/tools/Common/Internal/Runtime/ModuleHeaders.cs @@ -15,7 +15,7 @@ internal struct ReadyToRunHeaderConstants { public const uint Signature = 0x00525452; // 'RTR' - public const ushort CurrentMajorVersion = 21; + public const ushort CurrentMajorVersion = 22; public const ushort CurrentMinorVersion = 0; } #if READYTORUN diff --git a/src/coreclr/tools/Common/JitInterface/CorInfoTypes.VarInfo.cs b/src/coreclr/tools/Common/JitInterface/CorInfoTypes.VarInfo.cs index cd6d82f8f511e8..e307bcb2844616 100644 --- a/src/coreclr/tools/Common/JitInterface/CorInfoTypes.VarInfo.cs +++ b/src/coreclr/tools/Common/JitInterface/CorInfoTypes.VarInfo.cs @@ -34,6 +34,7 @@ public struct NativeVarInfo { public uint startOffset; public uint endOffset; + public uint callReturnValueILOffset; public uint varNumber; public VarLoc varLoc; }; diff --git a/src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs b/src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs index ff3dcf406ac6c6..f173125897d053 100644 --- a/src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs +++ b/src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs @@ -1361,15 +1361,16 @@ public struct OffsetMapping public enum ILNum { - 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 = -5, // Unknown variable + UNKNOWN_ILNUM = -6, // 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 the enum + // so that the compression routines know the enum's range. }; public struct ILVarInfo diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/DebugInfoTableNode.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/DebugInfoTableNode.cs index f2a1484bdeea91..0f58b1e2c97780 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/DebugInfoTableNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/DebugInfoTableNode.cs @@ -254,9 +254,17 @@ public static byte[] CreateVarBlobForMethod(NativeVarInfo[] varInfos, TargetDeta foreach (var nativeVarInfo in varInfos) { - writer.WriteUInt(nativeVarInfo.startOffset); - writer.WriteUInt(nativeVarInfo.endOffset - nativeVarInfo.startOffset); writer.WriteUInt((uint)(nativeVarInfo.varNumber - (int)ILNum.MAX_ILNUM)); + writer.WriteUInt(nativeVarInfo.startOffset); + + if (nativeVarInfo.varNumber == unchecked((uint)ILNum.CALL_RETURN_ILNUM)) + { + writer.WriteUInt(nativeVarInfo.callReturnValueILOffset); + } + else + { + writer.WriteUInt(nativeVarInfo.endOffset - nativeVarInfo.startOffset); + } VarLocType varLocType = nativeVarInfo.varLoc.LocationType; diff --git a/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/DebugInfo.cs b/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/DebugInfo.cs index bdb7bed772dade..58cb620fe98558 100644 --- a/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/DebugInfo.cs +++ b/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/DebugInfo.cs @@ -245,12 +245,39 @@ private void ParseNativeVarInfo(NativeReader imageReader, int offset) NibbleReader reader = new NibbleReader(imageReader, offset); uint nativeVarCount = reader.ReadUInt(); + int version = _runtimeFunction.ReadyToRunReader.ReadyToRunHeader.MajorVersion; + int implicitILAdjust = version switch + { + >= 22 => (int)ImplicitILArguments.MaxV22, + >= 20 => (int)ImplicitILArguments.MaxV20, + < 20 => (int)ImplicitILArguments.MaxV19, + }; + for (int i = 0; i < nativeVarCount; ++i) { var entry = new NativeVarInfo(); - entry.StartOffset = reader.ReadUInt(); - entry.EndOffset = entry.StartOffset + reader.ReadUInt(); - entry.VariableNumber = (uint)(reader.ReadUInt() + (int)ImplicitILArguments.Max); + + if (version >= 22) + { + entry.VariableNumber = (uint)(reader.ReadUInt() + implicitILAdjust); + entry.StartOffset = reader.ReadUInt(); + + if (entry.VariableNumber == unchecked((uint)ImplicitILArguments.CallReturnValue)) + { + entry.CallReturnValueILOffset = reader.ReadUInt(); + entry.EndOffset = entry.StartOffset + 1; + } + else + { + entry.EndOffset = entry.StartOffset + reader.ReadUInt(); + } + } + else + { + entry.StartOffset = reader.ReadUInt(); + entry.EndOffset = entry.StartOffset + reader.ReadUInt(); + entry.VariableNumber = (uint)(reader.ReadUInt() + implicitILAdjust); + } entry.Variable = new Variable(); // TODO: This is probably incomplete // This does not handle any implicit arguments or var args diff --git a/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/DebugInfoTypes.cs b/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/DebugInfoTypes.cs index 9232c0000a69d0..aa8e1e26d71e1e 100644 --- a/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/DebugInfoTypes.cs +++ b/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/DebugInfoTypes.cs @@ -17,6 +17,7 @@ public struct NativeVarInfo { public uint StartOffset; public uint EndOffset; + public uint CallReturnValueILOffset; // TODO: Eliminate this public uint VariableNumber; public Variable Variable { get; internal set; } @@ -82,8 +83,14 @@ public enum ImplicitILArguments VarArgsHandle = -1, ReturnBuffer = -2, TypeContext = -3, - Unknown = -4, - Max = Unknown + AsyncContinuation = -4, + CallReturnValue = -5, + Unknown = -6, + Max = Unknown, + + MaxV19 = -4, + MaxV20 = -5, + MaxV22 = -6, } public enum VarLocType diff --git a/src/coreclr/tools/r2rdump/Extensions.cs b/src/coreclr/tools/r2rdump/Extensions.cs index 010da8c0d96f72..f93e4cf36d2afb 100644 --- a/src/coreclr/tools/r2rdump/Extensions.cs +++ b/src/coreclr/tools/r2rdump/Extensions.cs @@ -78,6 +78,7 @@ public static void WriteTo(this DebugInfo theThis, TextWriter writer, DumpModel writer.WriteLine($" Variable Number: {varLoc.VariableNumber}"); writer.WriteLine($" Start Offset: 0x{varLoc.StartOffset:X}"); writer.WriteLine($" End Offset: 0x{varLoc.EndOffset:X}"); + writer.WriteLine($" Call return IL offset: 0x{varLoc.CallReturnValueILOffset:X}"); writer.WriteLine($" Loc Type: {varLoc.VariableLocation.VarLocType}"); switch (varLoc.VariableLocation.VarLocType) diff --git a/src/coreclr/vm/debuginfostore.cpp b/src/coreclr/vm/debuginfostore.cpp index 585bdabf8de52d..6e1f114c28288e 100644 --- a/src/coreclr/vm/debuginfostore.cpp +++ b/src/coreclr/vm/debuginfostore.cpp @@ -143,6 +143,9 @@ class TransferWriter void DoEncodedVarLocType(ICorDebugInfo::VarLocType & dw) { m_w.WriteEncodedU32(dw); } void DoEncodedUnsigned(unsigned & dw) { m_w.WriteEncodedU32(dw); } + void DoImplicitCallReturnValueILOffset(uint32_t& ilOffset) { } + void DoImplicitEndOffset(uint32_t& endOffset, uint32_t startOffset) { } + // Stack offsets are aligned on a DWORD boundary, so that lets us shave off 2 bits. void DoEncodedStackOffset(signed & dwOffset) { @@ -250,6 +253,17 @@ class TransferReader dw = (unsigned) m_r.ReadEncodedU32_NoThrow(); } + void DoImplicitCallReturnValueILOffset(uint32_t& ilOffset) + { + SUPPORTS_DAC; + ilOffset = 0; + } + + void DoImplicitEndOffset(uint32_t& endOffset, uint32_t startOffset) + { + SUPPORTS_DAC; + endOffset = startOffset + 1; + } // Stack offsets are aligned on a DWORD boundary, so that lets us shave off 2 bits. void DoEncodedStackOffset(signed & dwOffset) @@ -340,14 +354,21 @@ static void DoNativeVarInfo( // - VarLoc information. This is a tagged variant. // The entries aren't sorted in any particular order. trans.DoCookie(0xB); - trans.DoEncodedU32(pVar->startOffset); - - - trans.DoEncodedDeltaU32(pVar->endOffset, pVar->startOffset); - // record var number. - trans.DoEncodedAdjustedU32(pVar->varNumber, (DWORD) ICorDebugInfo::MAX_ILNUM); + trans.DoEncodedAdjustedU32(pVar->varNumber, (DWORD)ICorDebugInfo::MAX_ILNUM); + trans.DoEncodedU32(pVar->startOffset); + + if (pVar->varNumber == (DWORD)ICorDebugInfo::CALL_RETURN_ILNUM) + { + trans.DoEncodedU32(pVar->callReturnValueILOffset); + trans.DoImplicitEndOffset(pVar->endOffset, pVar->startOffset); + } + else + { + trans.DoEncodedDeltaU32(pVar->endOffset, pVar->startOffset); + trans.DoImplicitCallReturnValueILOffset(pVar->callReturnValueILOffset); + } // Now write the VarLoc... This is a variant like structure and so we'll get different // compressioned depending on what we've got. diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/Contracts/IDebugInfo.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/Contracts/IDebugInfo.cs index a05b2d4315740b..1d4ca1e505dbb9 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/Contracts/IDebugInfo.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/Contracts/IDebugInfo.cs @@ -72,6 +72,11 @@ public readonly struct DebugVarInfo public uint BaseRegister2 { get; init; } /// Second stack offset (RegisterStack). public int StackOffset2 { get; init; } + /// + /// For == ICorDebugInfo::CALL_RETURN_ILNUM entries, the IL offset of + /// the call site whose return value this entry describes. Zero for all other entries. + /// + public uint CallReturnValueILOffset { get; init; } } public interface IDebugInfo : IContract diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/DebugInfo/DebugInfoHelpers.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/DebugInfo/DebugInfoHelpers.cs index ab13d1f24b46af..244e5edec62ac4 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/DebugInfo/DebugInfoHelpers.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/DebugInfo/DebugInfoHelpers.cs @@ -41,7 +41,11 @@ private enum VarLocType private const uint SOURCE_TYPE_BITS_V2 = 3; // ICorDebugInfo::MAX_ILNUM sentinel value used for adjusted encoding of var numbers. - private const uint MAX_ILNUM = unchecked((uint)-5); + private const uint MAX_ILNUM = unchecked((uint)-6); + + // ICorDebugInfo::CALL_RETURN_ILNUM. Marks a NativeVarInfo whose location holds the + // return value of a call site. Such entries use a different on-wire layout (see DoVars). + private const uint CALL_RETURN_ILNUM = unchecked((uint)-5); internal static IEnumerable DoBounds(NativeReader nativeReader, uint version) { @@ -115,9 +119,19 @@ internal static IEnumerable DoVars(NativeReader nativeReader, bool for (uint i = 0; i < varCount; i++) { - uint startOffset = reader.ReadUInt(); - uint endOffset = startOffset + reader.ReadUInt(); uint varNumber = reader.ReadUInt() + MAX_ILNUM; + uint startOffset = reader.ReadUInt(); + uint endOffset; + uint callReturnValueILOffset = 0; + if (varNumber == CALL_RETURN_ILNUM) + { + callReturnValueILOffset = reader.ReadUInt(); + endOffset = startOffset + 1; + } + else + { + endOffset = startOffset + reader.ReadUInt(); + } VarLocType locType = (VarLocType)reader.ReadUInt(); if (locType is VarLocType.VLT_INVALID or VarLocType.VLT_COUNT) @@ -127,59 +141,59 @@ internal static IEnumerable DoVars(NativeReader nativeReader, bool { VarLocType.VLT_REG or VarLocType.VLT_REG_FP => new DebugVarInfo { - StartOffset = startOffset, EndOffset = endOffset, VarNumber = varNumber, + StartOffset = startOffset, EndOffset = endOffset, VarNumber = varNumber, CallReturnValueILOffset = callReturnValueILOffset, Kind = DebugVarLocKind.Register, Register = reader.ReadUInt(), }, VarLocType.VLT_REG_BYREF => new DebugVarInfo { - StartOffset = startOffset, EndOffset = endOffset, VarNumber = varNumber, + StartOffset = startOffset, EndOffset = endOffset, VarNumber = varNumber, CallReturnValueILOffset = callReturnValueILOffset, Kind = DebugVarLocKind.Register, Register = reader.ReadUInt(), IsByRef = true, }, VarLocType.VLT_STK => new DebugVarInfo { - StartOffset = startOffset, EndOffset = endOffset, VarNumber = varNumber, + StartOffset = startOffset, EndOffset = endOffset, VarNumber = varNumber, CallReturnValueILOffset = callReturnValueILOffset, Kind = DebugVarLocKind.Stack, BaseRegister = reader.ReadUInt(), StackOffset = ReadEncodedStackOffset(reader, isX86), }, VarLocType.VLT_STK_BYREF => new DebugVarInfo { - StartOffset = startOffset, EndOffset = endOffset, VarNumber = varNumber, + StartOffset = startOffset, EndOffset = endOffset, VarNumber = varNumber, CallReturnValueILOffset = callReturnValueILOffset, Kind = DebugVarLocKind.Stack, BaseRegister = reader.ReadUInt(), StackOffset = ReadEncodedStackOffset(reader, isX86), IsByRef = true, }, VarLocType.VLT_REG_REG => new DebugVarInfo { - StartOffset = startOffset, EndOffset = endOffset, VarNumber = varNumber, + StartOffset = startOffset, EndOffset = endOffset, VarNumber = varNumber, CallReturnValueILOffset = callReturnValueILOffset, Kind = DebugVarLocKind.RegisterRegister, Register = reader.ReadUInt(), Register2 = reader.ReadUInt(), }, VarLocType.VLT_REG_STK => new DebugVarInfo { - StartOffset = startOffset, EndOffset = endOffset, VarNumber = varNumber, + StartOffset = startOffset, EndOffset = endOffset, VarNumber = varNumber, CallReturnValueILOffset = callReturnValueILOffset, Kind = DebugVarLocKind.RegisterStack, Register = reader.ReadUInt(), BaseRegister2 = reader.ReadUInt(), StackOffset2 = ReadEncodedStackOffset(reader, isX86), }, VarLocType.VLT_STK_REG => new DebugVarInfo { - StartOffset = startOffset, EndOffset = endOffset, VarNumber = varNumber, + StartOffset = startOffset, EndOffset = endOffset, VarNumber = varNumber, CallReturnValueILOffset = callReturnValueILOffset, Kind = DebugVarLocKind.StackRegister, StackOffset = ReadEncodedStackOffset(reader, isX86), BaseRegister = reader.ReadUInt(), Register = reader.ReadUInt(), }, VarLocType.VLT_STK2 => new DebugVarInfo { - StartOffset = startOffset, EndOffset = endOffset, VarNumber = varNumber, + StartOffset = startOffset, EndOffset = endOffset, VarNumber = varNumber, CallReturnValueILOffset = callReturnValueILOffset, Kind = DebugVarLocKind.DoubleStack, BaseRegister = reader.ReadUInt(), StackOffset = ReadEncodedStackOffset(reader, isX86), }, // FPSTK and FIXED_VA: consume stream data to keep reader aligned, but no public // DebugVarLocKind exists for these rarely-used location types. - VarLocType.VLT_FPSTK => ConsumeAndDefault(reader.ReadUInt(), startOffset, endOffset, varNumber), - VarLocType.VLT_FIXED_VA => ConsumeAndDefault(reader.ReadUInt(), startOffset, endOffset, varNumber), + VarLocType.VLT_FPSTK => ConsumeAndDefault(reader.ReadUInt(), startOffset, endOffset, varNumber, callReturnValueILOffset), + VarLocType.VLT_FIXED_VA => ConsumeAndDefault(reader.ReadUInt(), startOffset, endOffset, varNumber, callReturnValueILOffset), _ => new DebugVarInfo { - StartOffset = startOffset, EndOffset = endOffset, VarNumber = varNumber, + StartOffset = startOffset, EndOffset = endOffset, VarNumber = varNumber, CallReturnValueILOffset = callReturnValueILOffset, }, }; } } - private static DebugVarInfo ConsumeAndDefault(uint _, uint startOffset, uint endOffset, uint varNumber) => new() + private static DebugVarInfo ConsumeAndDefault(uint _, uint startOffset, uint endOffset, uint varNumber, uint callReturnValueILOffset) => new() { - StartOffset = startOffset, EndOffset = endOffset, VarNumber = varNumber, + StartOffset = startOffset, EndOffset = endOffset, VarNumber = varNumber, CallReturnValueILOffset = callReturnValueILOffset, }; private static int ReadEncodedStackOffset(NibbleReader reader, bool isX86)