diff --git a/src/coreclr/debug/di/rsthread.cpp b/src/coreclr/debug/di/rsthread.cpp index 378bdbc44510c8..08c760735f91a1 100644 --- a/src/coreclr/debug/di/rsthread.cpp +++ b/src/coreclr/debug/di/rsthread.cpp @@ -8359,12 +8359,12 @@ HRESULT CordbJITILFrame::GetNativeVariable(CordbType *type, case ICorDebugInfo::VLT_REG_FP: #if defined(TARGET_ARM) // @ARMTODO hr = E_NOTIMPL; -#elif defined(TARGET_AMD64) +#elif defined(TARGET_AMD64) || defined(TARGET_ARM64) + // AMD64/ARM64 enumerate the FP registers in the debug RegNum enum + // (XMM0-15 / V0-31), so g_JITToCorDbgReg maps vlrReg directly to the + // corresponding CorDebugRegister. hr = m_nativeFrame->GetLocalFloatingPointValue(ConvertRegNumToCorDebugRegister(pNativeVarInfo->loc.vlReg.vlrReg), type, ppValue); -#elif defined(TARGET_ARM64) - hr = m_nativeFrame->GetLocalFloatingPointValue(pNativeVarInfo->loc.vlReg.vlrReg + REGISTER_ARM64_V0, - type, ppValue); #elif defined(TARGET_LOONGARCH64) hr = m_nativeFrame->GetLocalFloatingPointValue(pNativeVarInfo->loc.vlReg.vlrReg + REGISTER_LOONGARCH64_F0, type, ppValue); @@ -8400,7 +8400,7 @@ HRESULT CordbJITILFrame::GetNativeVariable(CordbType *type, break; case ICorDebugInfo::VLT_REG_REG: -#if defined(TARGET_AMD64) +#if defined(TARGET_AMD64) || defined(TARGET_ARM64) { const ICorDebugInfo::RegNum lowReg = pNativeVarInfo->loc.vlRegReg.vlrrReg1; const ICorDebugInfo::RegNum highReg = pNativeVarInfo->loc.vlRegReg.vlrrReg2; @@ -8409,9 +8409,9 @@ HRESULT CordbJITILFrame::GetNativeVariable(CordbType *type, if (lowIsFloat || highIsFloat) { - // AMD64 extends RegNum with XMM registers, so VLT_REG_REG can - // represent mixed int/fp pairs. Other targets still require - // dedicated encodings for FP-containing multi-register values. + // AMD64/ARM64 extend RegNum with FP registers (XMM/V), so + // VLT_REG_REG can represent mixed int/fp pairs. FP register + // indices for GetLocalTwoRegisterValue are 0-based. hr = m_nativeFrame->GetLocalTwoRegisterValue( lowIsFloat ? lowReg - ICorDebugInfo::REGNUM_FP_FIRST : ConvertRegNumToCorDebugRegister(lowReg), diff --git a/src/coreclr/debug/inc/arm64/primitives.h b/src/coreclr/debug/inc/arm64/primitives.h index cb7553be3c075e..4a7c89ae67920e 100644 --- a/src/coreclr/debug/inc/arm64/primitives.h +++ b/src/coreclr/debug/inc/arm64/primitives.h @@ -62,6 +62,7 @@ inline CORDB_ADDRESS GetPatchEndAddr(CORDB_ADDRESS patchAddr) constexpr CorDebugRegister g_JITToCorDbgReg[] = { + // Integer registers (must match RegNum order in cordebuginfo.h) REGISTER_ARM64_X0, REGISTER_ARM64_X1, REGISTER_ARM64_X2, @@ -94,7 +95,41 @@ constexpr CorDebugRegister g_JITToCorDbgReg[] = REGISTER_ARM64_FP, REGISTER_ARM64_LR, REGISTER_ARM64_SP, - REGISTER_ARM64_PC + REGISTER_ARM64_PC, + + // FP/SIMD V registers (V0-V31, starting at REGNUM_FP_FIRST) + REGISTER_ARM64_V0, + REGISTER_ARM64_V1, + REGISTER_ARM64_V2, + REGISTER_ARM64_V3, + REGISTER_ARM64_V4, + REGISTER_ARM64_V5, + REGISTER_ARM64_V6, + REGISTER_ARM64_V7, + REGISTER_ARM64_V8, + REGISTER_ARM64_V9, + REGISTER_ARM64_V10, + REGISTER_ARM64_V11, + REGISTER_ARM64_V12, + REGISTER_ARM64_V13, + REGISTER_ARM64_V14, + REGISTER_ARM64_V15, + REGISTER_ARM64_V16, + REGISTER_ARM64_V17, + REGISTER_ARM64_V18, + REGISTER_ARM64_V19, + REGISTER_ARM64_V20, + REGISTER_ARM64_V21, + REGISTER_ARM64_V22, + REGISTER_ARM64_V23, + REGISTER_ARM64_V24, + REGISTER_ARM64_V25, + REGISTER_ARM64_V26, + REGISTER_ARM64_V27, + REGISTER_ARM64_V28, + REGISTER_ARM64_V29, + REGISTER_ARM64_V30, + REGISTER_ARM64_V31, }; inline void CORDbgSetIP(DT_CONTEXT *context, LPVOID eip) { diff --git a/src/coreclr/debug/inc/dbgipcevents.h b/src/coreclr/debug/inc/dbgipcevents.h index f66c68d17073c6..8a087492ca3d17 100644 --- a/src/coreclr/debug/inc/dbgipcevents.h +++ b/src/coreclr/debug/inc/dbgipcevents.h @@ -1449,7 +1449,7 @@ static_assert(DBG_TARGET_REGNUM_AMBIENT_SP == ICorDebugInfo::REGNUM_AMBIENT_SP); #endif // TARGET_ARM #elif defined(TARGET_ARM64) #define DBG_TARGET_REGNUM_SP 31 -#define DBG_TARGET_REGNUM_AMBIENT_SP 34 +#define DBG_TARGET_REGNUM_AMBIENT_SP 66 #ifdef TARGET_ARM64 static_assert(DBG_TARGET_REGNUM_SP == ICorDebugInfo::REGNUM_SP); static_assert(DBG_TARGET_REGNUM_AMBIENT_SP == ICorDebugInfo::REGNUM_AMBIENT_SP); diff --git a/src/coreclr/inc/cordebuginfo.h b/src/coreclr/inc/cordebuginfo.h index f5975160b84a80..bc0d5cc5c9a69f 100644 --- a/src/coreclr/inc/cordebuginfo.h +++ b/src/coreclr/inc/cordebuginfo.h @@ -131,6 +131,41 @@ class ICorDebugInfo REGNUM_LR, REGNUM_SP, REGNUM_PC, + + // SIMD/FP V registers + REGNUM_FP_FIRST, + REGNUM_V0 = REGNUM_FP_FIRST, + REGNUM_V1, + REGNUM_V2, + REGNUM_V3, + REGNUM_V4, + REGNUM_V5, + REGNUM_V6, + REGNUM_V7, + REGNUM_V8, + REGNUM_V9, + REGNUM_V10, + REGNUM_V11, + REGNUM_V12, + REGNUM_V13, + REGNUM_V14, + REGNUM_V15, + REGNUM_V16, + REGNUM_V17, + REGNUM_V18, + REGNUM_V19, + REGNUM_V20, + REGNUM_V21, + REGNUM_V22, + REGNUM_V23, + REGNUM_V24, + REGNUM_V25, + REGNUM_V26, + REGNUM_V27, + REGNUM_V28, + REGNUM_V29, + REGNUM_V30, + REGNUM_V31, #elif TARGET_AMD64 REGNUM_RAX, REGNUM_RCX, diff --git a/src/coreclr/inc/jiteeversionguid.h b/src/coreclr/inc/jiteeversionguid.h index e48e375315e0f5..08a3bcde5744d6 100644 --- a/src/coreclr/inc/jiteeversionguid.h +++ b/src/coreclr/inc/jiteeversionguid.h @@ -37,11 +37,11 @@ #include -constexpr GUID JITEEVersionIdentifier = { /* 65743063-e8fa-41d4-9496-c436974c00f5 */ - 0x65743063, - 0xe8fa, - 0x41d4, - {0x94, 0x96, 0xc4, 0x36, 0x97, 0x4c, 0x00, 0xf5} +constexpr GUID JITEEVersionIdentifier = { /* 5bb9b1e5-9941-4762-a195-351b6a736588 */ + 0x5bb9b1e5, + 0x9941, + 0x4762, + {0xa1, 0x95, 0x35, 0x1b, 0x6a, 0x73, 0x65, 0x88} }; #endif // JIT_EE_VERSIONING_GUID_H diff --git a/src/coreclr/inc/readytorun.h b/src/coreclr/inc/readytorun.h index 0de724ff89d2bd..16eedd1c8a1829 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 25 +#define READYTORUN_MAJOR_VERSION 26 #define READYTORUN_MINOR_VERSION 0x0000 -#define MINIMUM_READYTORUN_MAJOR_VERSION 25 +#define MINIMUM_READYTORUN_MAJOR_VERSION 26 // R2R Version 2.1 adds the InliningInfo section // R2R Version 2.2 adds the ProfileDataInfo section @@ -65,6 +65,7 @@ // R2R Version 23 changes delegate layout to have target before methodPtr // R2R Version 24 changes ARM32 virtual stub dispatch hidden parameter register to R12 // R2R Version 25 renames runtime async infrastructure members, makes thunk-used members NonVersionable, and frees up a flag in CorInfoContinuationFlags +// R2R Version 26 changes ARM64 NativeVarInfo register encoding to include V0-V31 struct READYTORUN_CORE_HEADER { diff --git a/src/coreclr/jit/codegencommon.cpp b/src/coreclr/jit/codegencommon.cpp index 5d9b722b39227e..7f3cdc29802214 100644 --- a/src/coreclr/jit/codegencommon.cpp +++ b/src/coreclr/jit/codegencommon.cpp @@ -1831,6 +1831,18 @@ void CodeGen::genEmitCallWithCurrentGC(EmitCallParams& params) regNumber reg1 = retDesc->GetABIReturnReg(0, call->GetUnmanagedCallConv()); regNumber reg2 = retDesc->GetABIReturnReg(1, call->GetUnmanagedCallConv()); +#ifdef TARGET_ARM64 + if ((!genIsValidIntReg(reg1) || !genIsValidIntReg(reg2)) && + (retDesc->GetReturnFieldOffset(1) != TARGET_POINTER_SIZE)) + { + // The two-register debug-info representation places the second + // register at the next pointer-sized offset. ARM64 HFAs/HVAs with + // smaller or larger elements use a different offset and cannot be + // represented without recording per-register piece sizes. + return; + } +#endif + #if !defined(TARGET_64BIT) // Multi-register debug-info encodings that involve floating-point // registers assume each register holds an 8-byte half of the value, so @@ -1850,11 +1862,13 @@ void CodeGen::genEmitCallWithCurrentGC(EmitCallParams& params) { return; } -#elif !defined(TARGET_AMD64) - // This unified RegNum encoding is implemented only for AMD64. Other 64-bit - // targets still need dedicated encodings to represent FP-containing - // two-register returns without ambiguity, so suppress those cases here - // instead of emitting an encoding the debugger cannot decode. +#elif !defined(TARGET_AMD64) && !defined(TARGET_ARM64) + // AMD64 and ARM64 include FP registers in their debug RegNum enumerations, + // so VLT_REG_REG can encode FP-containing two-register returns. Other + // 64-bit targets do not yet implement the debugger-side floating-point + // read path, so suppress those cases instead of emitting an encoding the + // debugger cannot decode. A pair of integer registers is still encoded + // below as VLT_REG_REG. if (!genIsValidIntReg(reg1) || !genIsValidIntReg(reg2)) { return; diff --git a/src/coreclr/jit/ee_il_dll.cpp b/src/coreclr/jit/ee_il_dll.cpp index 4258e568e62075..47af3c1f9b7e06 100644 --- a/src/coreclr/jit/ee_il_dll.cpp +++ b/src/coreclr/jit/ee_il_dll.cpp @@ -902,7 +902,9 @@ void Compiler::eeDispVar(ICorDebugInfo::NativeVarInfo* var) break; case CodeGenInterface::VLT_REG_FP: -#ifdef TARGET_AMD64 +#if defined(TARGET_AMD64) || defined(TARGET_ARM64) + // AMD64/ARM64 store the FP register as a debug RegNum (REGNUM_FP_FIRST-based); + // map it back to a JIT regNumber for display. printf("%s", getRegName(static_cast(REG_FP_FIRST + var->loc.vlReg.vlrReg - ICorDebugInfo::REGNUM_FP_FIRST))); #else @@ -928,7 +930,7 @@ void Compiler::eeDispVar(ICorDebugInfo::NativeVarInfo* var) case CodeGenInterface::VLT_REG_REG: { -#ifdef TARGET_AMD64 +#if defined(TARGET_AMD64) || defined(TARGET_ARM64) auto toJitRegNum = [](ICorDebugInfo::RegNum reg) -> regNumber { unsigned val = static_cast(reg); unsigned fpFirst = static_cast(ICorDebugInfo::REGNUM_FP_FIRST); diff --git a/src/coreclr/jit/scopeinfo.cpp b/src/coreclr/jit/scopeinfo.cpp index debf14770c4a0c..edae8a122f9693 100644 --- a/src/coreclr/jit/scopeinfo.cpp +++ b/src/coreclr/jit/scopeinfo.cpp @@ -160,9 +160,13 @@ bool CodeGenInterface::siVarLoc::vlIsOnStack() const // static ICorDebugInfo::RegNum CodeGenInterface::siVarLoc::mapRegNumToDebugRegNum(regNumber reg) { +#if defined(TARGET_AMD64) || defined(TARGET_ARM64) + constexpr unsigned fpRegDebugNumBase = ICorDebugInfo::REGNUM_FP_FIRST; #ifdef TARGET_AMD64 - constexpr unsigned fpRegDebugNumBase = ICorDebugInfo::REGNUM_FP_FIRST; - constexpr unsigned maxEncodableFpRegs = 16; // Only XMM0-XMM15 are in RegNum + constexpr unsigned maxEncodableFpRegs = 16; // Only XMM0-XMM15 +#else + constexpr unsigned maxEncodableFpRegs = 32; // V0-V31 +#endif #else constexpr unsigned fpRegDebugNumBase = 0; constexpr unsigned maxEncodableFpRegs = 0; @@ -176,9 +180,7 @@ ICorDebugInfo::RegNum CodeGenInterface::siVarLoc::mapRegNumToDebugRegNum(regNumb if (genIsValidFloatReg(reg)) { unsigned fpIndex = reg - REG_FP_FIRST; -#ifdef TARGET_AMD64 - // Only XMM0-XMM15 are representable in the debug RegNum enum. - // XMM16-XMM31 (AVX-512) cannot be encoded. +#if defined(TARGET_AMD64) || defined(TARGET_ARM64) if (fpIndex >= maxEncodableFpRegs) { return ICorDebugInfo::REGNUM_COUNT; // sentinel: caller checks for this @@ -210,18 +212,22 @@ void CodeGenInterface::siVarLoc::storeVariableInRegisters(regNumber reg, regNumb { if (genIsValidFloatReg(reg)) { -#ifdef TARGET_AMD64 +#if defined(TARGET_AMD64) || defined(TARGET_ARM64) + // AMD64/ARM64 enumerate the FP registers in the debug RegNum enum + // (XMM0-15 / V0-31), so store the mapped RegNum. This keeps the single-FP + // encoding identical to getSiVarLoc and lets the DBI decode uniformly via + // ConvertRegNumToCorDebugRegister. ICorDebugInfo::RegNum debugReg = mapRegNumToDebugRegNum(reg); if (debugReg == ICorDebugInfo::REGNUM_COUNT) { - // XMM16+ cannot be encoded in the debug info. + // The FP register is not representable in the debug RegNum enum. vlType = VLT_INVALID; return; } vlType = VLT_REG_FP; vlReg.vlrReg = static_cast(debugReg); #else - // Non-AMD64: store 0-based FP register index (DBI adds platform base) + // Other targets: store 0-based FP register index (DBI adds platform base) vlType = VLT_REG_FP; vlReg.vlrReg = static_cast(reg - REG_FP_FIRST); #endif @@ -240,7 +246,7 @@ void CodeGenInterface::siVarLoc::storeVariableInRegisters(regNumber reg, regNumb } else { -#ifdef TARGET_AMD64 +#if defined(TARGET_AMD64) || defined(TARGET_ARM64) ICorDebugInfo::RegNum debugReg1 = mapRegNumToDebugRegNum(reg); ICorDebugInfo::RegNum debugReg2 = mapRegNumToDebugRegNum(otherReg); if (debugReg1 == ICorDebugInfo::REGNUM_COUNT || debugReg2 == ICorDebugInfo::REGNUM_COUNT) @@ -252,8 +258,8 @@ void CodeGenInterface::siVarLoc::storeVariableInRegisters(regNumber reg, regNumb vlRegReg.vlrrReg1 = static_cast(debugReg1); vlRegReg.vlrrReg2 = static_cast(debugReg2); #else - // Non-AMD64: VLT_REG_REG only supports int registers. If either is FP, - // we cannot encode this — fall back to VLT_INVALID. + // Other non-AMD64 targets: VLT_REG_REG only supports int registers. If either + // is FP, we cannot encode this — fall back to VLT_INVALID. if (!genIsValidIntReg(reg) || !genIsValidIntReg(otherReg)) { vlType = VLT_INVALID; @@ -634,7 +640,9 @@ void CodeGenInterface::dumpSiVarLoc(const siVarLoc* varLoc) const break; case VLT_REG_FP: -#ifdef TARGET_AMD64 +#if defined(TARGET_AMD64) || defined(TARGET_ARM64) + // AMD64/ARM64 store the FP register as a debug RegNum (REGNUM_FP_FIRST-based); + // map it back to a JIT regNumber for display. printf("%s", getRegName(static_cast(REG_FP_FIRST + varLoc->vlReg.vlrReg - ICorDebugInfo::REGNUM_FP_FIRST))); #else @@ -660,7 +668,7 @@ void CodeGenInterface::dumpSiVarLoc(const siVarLoc* varLoc) const break; case VLT_REG_REG: -#ifdef TARGET_AMD64 +#if defined(TARGET_AMD64) || defined(TARGET_ARM64) { // Map RegNum values (which may include FP register indices) back to // JIT regNumber for display purposes. diff --git a/src/coreclr/nativeaot/Runtime/inc/ModuleHeaders.h b/src/coreclr/nativeaot/Runtime/inc/ModuleHeaders.h index 9e8e5b1f3d2afe..573be41688fe44 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 = 25; + static const uint32_t CurrentMajorVersion = 26; 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 64890296b5eba9..acf200fdd2f339 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 = 25; + public const ushort CurrentMajorVersion = 26; public const ushort CurrentMinorVersion = 0; } #if READYTORUN diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/CodeView/CodeViewSymbolsBuilder.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/CodeView/CodeViewSymbolsBuilder.cs index 2ca792fd6645a1..3f26c367619664 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/CodeView/CodeViewSymbolsBuilder.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/CodeView/CodeViewSymbolsBuilder.cs @@ -100,10 +100,16 @@ private CodeViewRegister GetCVRegNum(uint regNum) }; case TargetArchitecture.ARM64: - // X0-X28, FP, LR, SP have same order - if (regNum <= 32) + // X0-X28, FP, LR, SP have the same order. + if (regNum <= 31) return (CodeViewRegister)(regNum + (uint)CV_ARM64_X0); - // TODO: Floating point + + if (regNum == 32) + return CV_ARM64_PC; + + if (regNum <= 64) + return (CodeViewRegister)(regNum - 33 + (uint)CV_ARM64_Q0); + return CV_REG_NONE; default: diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/Dwarf/DwarfExpressionBuilder.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/Dwarf/DwarfExpressionBuilder.cs index 44600c8861d6d6..7518d0d9e1df47 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/Dwarf/DwarfExpressionBuilder.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/Dwarf/DwarfExpressionBuilder.cs @@ -94,7 +94,7 @@ private static int AmbientSpRegNum(TargetArchitecture architecture) { TargetArchitecture.X86 => (int)RegNumX86.REGNUM_COUNT + 1, TargetArchitecture.X64 => (int)RegNumAmd64.REGNUM_COUNT + 1, - TargetArchitecture.ARM64 => 34, // 33 int registers (X0-X28, FP, LR, SP, PC), +1 + TargetArchitecture.ARM64 => 66, // 33 int registers + 32 V registers, +1 TargetArchitecture.ARM => 17, // 16 int registers (R0-R12, SP, LR, PC), +1 TargetArchitecture.LoongArch64 => 34, // 33 int registers, +1 TargetArchitecture.RiscV64 => 34, // 33 int registers, +1 diff --git a/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/Arm64/Registers.cs b/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/Arm64/Registers.cs index 0fc02d1ba595bc..2d5e8d1cf4a87c 100644 --- a/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/Arm64/Registers.cs +++ b/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/Arm64/Registers.cs @@ -40,5 +40,39 @@ public enum Registers X28, X29, X30, + SP, + PC, + V0, + V1, + V2, + V3, + V4, + V5, + V6, + V7, + V8, + V9, + V10, + V11, + V12, + V13, + V14, + V15, + V16, + V17, + V18, + V19, + V20, + V21, + V22, + V23, + V24, + V25, + V26, + V27, + V28, + V29, + V30, + V31, } } diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/ARM64Context.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/ARM64Context.cs index 8016c0ac1765a2..df68a698e77094 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/ARM64Context.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/ARM64Context.cs @@ -158,6 +158,12 @@ public bool TryReadRegister(string name, out TargetNUInt value) public bool TrySetRegister(int number, TargetNUInt value) { + if ((uint)(number - 33) < 32) + { + SetVectorRegister(number - 33, value); + return true; + } + switch (number) { case 0: X0 = value.Value; return true; @@ -199,6 +205,12 @@ public bool TrySetRegister(int number, TargetNUInt value) public bool TryReadRegister(int number, out TargetNUInt value) { + if ((uint)(number - 33) < 32) + { + value = ReadVectorRegister(number - 33); + return true; + } + switch (number) { case 0: value = new TargetNUInt(X0); return true; @@ -238,6 +250,22 @@ public bool TryReadRegister(int number, out TargetNUInt value) } } + private unsafe void SetVectorRegister(int index, TargetNUInt value) + { + fixed (ulong* registers = V) + { + registers[index * 2] = value.Value; + } + } + + private readonly unsafe TargetNUInt ReadVectorRegister(int index) + { + fixed (ulong* registers = V) + { + return new TargetNUInt(registers[index * 2]); + } + } + // Control flags [FieldOffset(0x0)] diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/ClrDataFrame.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/ClrDataFrame.cs index 1052e9519020cd..c76be00dbb2b03 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/ClrDataFrame.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/ClrDataFrame.cs @@ -948,16 +948,33 @@ private static ulong ReadRegister(IPlatformAgnosticContext context, Target targe if (context.TryReadRegister((int)registerNumber, out TargetNUInt value)) return value.Value; - // REGNUM_AMBIENT_SP is beyond the normal register range on every architecture. - // It represents the entry-time SP, not necessarily the current SP. - // Map it to the stack pointer as a best-effort approximation (see util.cpp). - int spRegisterNumber = GetStackPointerRegisterNumber(target); - if (spRegisterNumber >= 0 && context.TryReadRegister(spRegisterNumber, out value)) - return value.Value; + if (registerNumber == GetAmbientStackPointerRegisterNumber(target)) + { + // REGNUM_AMBIENT_SP represents the entry-time SP, not necessarily the + // current SP. Map it to SP as a best-effort approximation (see util.cpp). + int spRegisterNumber = GetStackPointerRegisterNumber(target); + if (spRegisterNumber >= 0 && context.TryReadRegister(spRegisterNumber, out value)) + return value.Value; + } return 0; } + private static uint GetAmbientStackPointerRegisterNumber(Target target) + { + RuntimeInfoArchitecture arch = target.Contracts.RuntimeInfo.GetTargetArchitecture(); + return arch switch + { + RuntimeInfoArchitecture.X64 => 33, + RuntimeInfoArchitecture.X86 => 9, + RuntimeInfoArchitecture.Arm64 => 66, + RuntimeInfoArchitecture.Arm => 17, + RuntimeInfoArchitecture.LoongArch64 => 34, + RuntimeInfoArchitecture.RiscV64 => 34, + _ => uint.MaxValue, + }; + } + private static int GetStackPointerRegisterNumber(Target target) { RuntimeInfoArchitecture arch = target.Contracts.RuntimeInfo.GetTargetArchitecture(); diff --git a/src/native/managed/cdac/tests/UnitTests/PlatformContextTests.cs b/src/native/managed/cdac/tests/UnitTests/PlatformContextTests.cs index 3adbf371674f94..1c8ff03b5ccb20 100644 --- a/src/native/managed/cdac/tests/UnitTests/PlatformContextTests.cs +++ b/src/native/managed/cdac/tests/UnitTests/PlatformContextTests.cs @@ -36,6 +36,8 @@ public void AMD64_OutOfRange_ReturnsFalse(int regNum) [InlineData(29, 0xABCDUL)] // Fp [InlineData(31, 0x5678UL)] // Sp [InlineData(32, 0x9000UL)] // Pc + [InlineData(33, 0x1122334455667788UL)] // V0, low 64 bits + [InlineData(64, 0x8877665544332211UL)] // V31, low 64 bits public void ARM64_TrySetAndRead_ByNumber_RoundTrips(int regNum, ulong testValue) { var ctx = new ARM64Context(); @@ -45,7 +47,8 @@ public void ARM64_TrySetAndRead_ByNumber_RoundTrips(int regNum, ulong testValue) } [Theory] - [InlineData(33)] + [InlineData(65)] // REGNUM_COUNT + [InlineData(66)] // REGNUM_AMBIENT_SP public void ARM64_OutOfRange_ReturnsFalse(int regNum) { var ctx = new ARM64Context(); @@ -53,6 +56,23 @@ public void ARM64_OutOfRange_ReturnsFalse(int regNum) Assert.False(ctx.TryReadRegister(regNum, out _)); } + [Fact] + public unsafe void ARM64_VectorRegisterNumber_UsesLow64Bits() + { + var ctx = new ARM64Context(); + ulong* registers = ctx.V; + registers[1] = 0x1111; + registers[3] = 0x3333; + registers[4] = 0x4444; + + Assert.True(ctx.TrySetRegister(34, new TargetNUInt(0x2222))); + + Assert.Equal(0x1111UL, registers[1]); + Assert.Equal(0x2222UL, registers[2]); + Assert.Equal(0x3333UL, registers[3]); + Assert.Equal(0x4444UL, registers[4]); + } + [Theory] [InlineData(0, 0x12U)] // R0 [InlineData(13, 0x100U)] // Sp