diff --git a/eng/Subsets.props b/eng/Subsets.props index 68569344a6ea0..4347c0fbf4e45 100644 --- a/eng/Subsets.props +++ b/eng/Subsets.props @@ -120,7 +120,7 @@ <_NativeAotSupportedOS Condition="'$(TargetOS)' == 'windows' or '$(TargetOS)' == 'linux' or '$(TargetOS)' == 'osx' or '$(TargetOS)' == 'maccatalyst' or '$(TargetOS)' == 'iossimulator' or '$(TargetOS)' == 'ios' or '$(TargetOS)' == 'tvossimulator' or '$(TargetOS)' == 'tvos' or '$(TargetOS)' == 'freebsd'">true - <_NativeAotSupportedArch Condition="'$(TargetArchitecture)' == 'x64' or '$(TargetArchitecture)' == 'arm64' or '$(TargetArchitecture)' == 'arm' or ('$(TargetOS)' == 'windows' and '$(TargetArchitecture)' == 'x86')">true + <_NativeAotSupportedArch Condition="'$(TargetArchitecture)' == 'x64' or '$(TargetArchitecture)' == 'arm64' or '$(TargetArchitecture)' == 'arm' or '$(TargetArchitecture)' == 'loongarch64' or ('$(TargetOS)' == 'windows' and '$(TargetArchitecture)' == 'x86')">true true true diff --git a/src/coreclr/CMakeLists.txt b/src/coreclr/CMakeLists.txt index 808aac13e22cd..2e9a6c76743e5 100644 --- a/src/coreclr/CMakeLists.txt +++ b/src/coreclr/CMakeLists.txt @@ -150,7 +150,7 @@ add_subdirectory(tools/aot/jitinterface) if(NOT CLR_CROSS_COMPONENTS_BUILD) # NativeAOT only buildable for a subset of CoreCLR-supported configurations - if(CLR_CMAKE_HOST_ARCH_ARM64 OR CLR_CMAKE_HOST_ARCH_AMD64 OR CLR_CMAKE_HOST_ARCH_ARM OR (CLR_CMAKE_HOST_ARCH_I386 AND CLR_CMAKE_HOST_WIN32)) + if(CLR_CMAKE_HOST_ARCH_ARM64 OR CLR_CMAKE_HOST_ARCH_AMD64 OR CLR_CMAKE_HOST_ARCH_ARM OR CLR_CMAKE_HOST_ARCH_LOONGARCH64 OR (CLR_CMAKE_HOST_ARCH_I386 AND CLR_CMAKE_HOST_WIN32)) add_subdirectory(nativeaot) endif() endif(NOT CLR_CROSS_COMPONENTS_BUILD) diff --git a/src/coreclr/nativeaot/Common/src/Internal/Runtime/TransitionBlock.cs b/src/coreclr/nativeaot/Common/src/Internal/Runtime/TransitionBlock.cs index 98126202f1164..ccff78114d4e9 100644 --- a/src/coreclr/nativeaot/Common/src/Internal/Runtime/TransitionBlock.cs +++ b/src/coreclr/nativeaot/Common/src/Internal/Runtime/TransitionBlock.cs @@ -35,6 +35,12 @@ #define ENREGISTERED_RETURNTYPE_INTEGER_MAXSIZE #define ENREGISTERED_PARAMTYPE_MAXSIZE #elif TARGET_WASM +#elif TARGET_LOONGARCH64 +#define CALLDESCR_ARGREGS // CallDescrWorker has ArgumentRegister parameter +#define CALLDESCR_FPARGREGS // CallDescrWorker has FloatArgumentRegisters parameter +#define ENREGISTERED_RETURNTYPE_MAXSIZE +#define ENREGISTERED_RETURNTYPE_INTEGER_MAXSIZE +#define ENREGISTERED_PARAMTYPE_MAXSIZE #else #error Unknown architecture! #endif @@ -300,6 +306,60 @@ internal struct ArchitectureConstants public const int STACK_ELEM_SIZE = 4; public static int StackElemSize(int size) { return (((size) + STACK_ELEM_SIZE - 1) & ~(STACK_ELEM_SIZE - 1)); } } +#elif TARGET_LOONGARCH64 + [StructLayout(LayoutKind.Sequential)] + internal struct ReturnBlock + { + private IntPtr returnValue; + private IntPtr returnValue2; + private IntPtr returnValue3; + private IntPtr returnValue4; + } + + [StructLayout(LayoutKind.Sequential)] + internal struct ArgumentRegisters + { + private IntPtr r4; + private IntPtr r5; + private IntPtr r6; + private IntPtr r7; + private IntPtr r8; + private IntPtr r9; + private IntPtr r10; + private IntPtr r11; + public static unsafe int GetOffsetOfr11() + { + return sizeof(IntPtr) * 7; + } + } + + [StructLayout(LayoutKind.Sequential)] + internal struct FloatArgumentRegisters + { + private double f0; + private double f1; + private double f2; + private double f3; + private double f4; + private double f5; + private double f6; + private double f7; + } + + internal struct ArchitectureConstants + { + // To avoid corner case bugs, limit maximum size of the arguments with sufficient margin + public const int MAX_ARG_SIZE = 0xFFFFFF; + + public const int NUM_ARGUMENT_REGISTERS = 8; + public const int ARGUMENTREGISTERS_SIZE = NUM_ARGUMENT_REGISTERS * 8; + public const int ENREGISTERED_RETURNTYPE_MAXSIZE = 32; // bytes (four FP registers: d0,d1,d2 and d3) + public const int ENREGISTERED_RETURNTYPE_INTEGER_MAXSIZE = 16; // bytes (two int registers: x0 and x1) + public const int ENREGISTERED_RETURNTYPE_INTEGER_MAXSIZE_PRIMITIVE = 8; + public const int ENREGISTERED_PARAMTYPE_MAXSIZE = 16; // bytes (max value type size that can be passed by value) + public const int STACK_ELEM_SIZE = 8; + public static int StackElemSize(int size) { return (((size) + STACK_ELEM_SIZE - 1) & ~(STACK_ELEM_SIZE - 1)); } + } #endif // @@ -392,6 +452,20 @@ public static unsafe int GetOffsetOfArgumentRegisters() { return sizeof(ReturnBlock); } +#elif TARGET_LOONGARCH64 + public ReturnBlock m_returnBlock; + public static unsafe int GetOffsetOfReturnValuesBlock() + { + return 0; + } + + public ArgumentRegisters m_argumentRegisters; + public static unsafe int GetOffsetOfArgumentRegisters() + { + return sizeof(ReturnBlock); + } + + public IntPtr m_alignmentPad; #else #error Portability problem #endif diff --git a/src/coreclr/nativeaot/Directory.Build.props b/src/coreclr/nativeaot/Directory.Build.props index b06c29baeb43e..c01756cfc8aba 100644 --- a/src/coreclr/nativeaot/Directory.Build.props +++ b/src/coreclr/nativeaot/Directory.Build.props @@ -89,6 +89,9 @@ TARGET_64BIT;TARGET_ARM64;$(DefineConstants) + + TARGET_64BIT;TARGET_LOONGARCH64;$(DefineConstants) + TARGET_WINDOWS;$(DefineConstants) diff --git a/src/coreclr/nativeaot/Runtime/CommonMacros.h b/src/coreclr/nativeaot/Runtime/CommonMacros.h index fe6a081bbecb9..be6556ba15351 100644 --- a/src/coreclr/nativeaot/Runtime/CommonMacros.h +++ b/src/coreclr/nativeaot/Runtime/CommonMacros.h @@ -119,6 +119,11 @@ inline bool IS_ALIGNED(T* val, uintptr_t alignment); #define LOG2_PTRSIZE 2 #define POINTER_SIZE 4 +#elif defined(HOST_LOONGARCH64) + +#define LOG2_PTRSIZE 3 +#define POINTER_SIZE 8 + #else #error Unsupported target architecture #endif diff --git a/src/coreclr/nativeaot/Runtime/EHHelpers.cpp b/src/coreclr/nativeaot/Runtime/EHHelpers.cpp index 325128c4e01fc..de35a123e012a 100644 --- a/src/coreclr/nativeaot/Runtime/EHHelpers.cpp +++ b/src/coreclr/nativeaot/Runtime/EHHelpers.cpp @@ -187,6 +187,22 @@ FCIMPL3(void, RhpCopyContextFromExInfo, void * pOSContext, int32_t cbOSContext, pContext->Sp = pPalContext->SP; pContext->Lr = pPalContext->LR; pContext->Pc = pPalContext->IP; +#elif defined(HOST_LOONGARCH64) + pContext->R4 = pPalContext->R4; + pContext->R5 = pPalContext->R5; + pContext->R23 = pPalContext->R23; + pContext->R24 = pPalContext->R24; + pContext->R25 = pPalContext->R25; + pContext->R26 = pPalContext->R26; + pContext->R27 = pPalContext->R27; + pContext->R28 = pPalContext->R28; + pContext->R29 = pPalContext->R29; + pContext->R30 = pPalContext->R30; + pContext->R31 = pPalContext->R31; + pContext->Fp = pPalContext->FP; + pContext->Sp = pPalContext->SP; + pContext->Ra = pPalContext->RA; + pContext->Pc = pPalContext->IP; #elif defined(HOST_WASM) // No registers, no work to do yet #else @@ -195,7 +211,6 @@ FCIMPL3(void, RhpCopyContextFromExInfo, void * pOSContext, int32_t cbOSContext, } FCIMPLEND -#if defined(HOST_AMD64) || defined(HOST_ARM) || defined(HOST_X86) || defined(HOST_ARM64) struct DISPATCHER_CONTEXT { uintptr_t ControlPc; @@ -257,56 +272,8 @@ EXTERN_C int32_t __stdcall RhpPInvokeExceptionGuard(PEXCEPTION_RECORD pExc return 0; } -#else -EXTERN_C int32_t RhpPInvokeExceptionGuard() -{ - ASSERT_UNCONDITIONALLY("RhpPInvokeExceptionGuard NYI for this architecture!"); - RhFailFast(); - return 0; -} -#endif -#if defined(HOST_AMD64) || defined(HOST_ARM) || defined(HOST_X86) || defined(HOST_ARM64) || defined(HOST_WASM) FCDECL2(void, RhpThrowHwEx, int exceptionCode, TADDR faultingIP); -#else -FCIMPL0(void, RhpThrowHwEx) -{ - ASSERT_UNCONDITIONALLY("RhpThrowHwEx NYI for this architecture!"); -} -FCIMPLEND -FCIMPL0(void, RhpThrowEx) -{ - ASSERT_UNCONDITIONALLY("RhpThrowEx NYI for this architecture!"); -} -FCIMPLEND -FCIMPL0(void, RhpCallCatchFunclet) -{ - ASSERT_UNCONDITIONALLY("RhpCallCatchFunclet NYI for this architecture!"); -} -FCIMPLEND -FCIMPL0(void, RhpCallFinallyFunclet) -{ - ASSERT_UNCONDITIONALLY("RhpCallFinallyFunclet NYI for this architecture!"); -} -FCIMPLEND -FCIMPL0(void, RhpCallFilterFunclet) -{ - ASSERT_UNCONDITIONALLY("RhpCallFilterFunclet NYI for this architecture!"); -} -FCIMPLEND -FCIMPL0(void, RhpRethrow) -{ - ASSERT_UNCONDITIONALLY("RhpRethrow NYI for this architecture!"); -} -FCIMPLEND - -EXTERN_C void* RhpCallCatchFunclet2 = NULL; -EXTERN_C void* RhpCallFinallyFunclet2 = NULL; -EXTERN_C void* RhpCallFilterFunclet2 = NULL; -EXTERN_C void* RhpThrowEx2 = NULL; -EXTERN_C void* RhpThrowHwEx2 = NULL; -EXTERN_C void* RhpRethrow2 = NULL; -#endif EXTERN_C CODE_LOCATION RhpAssignRefAVLocation; #if defined(HOST_X86) @@ -328,7 +295,7 @@ EXTERN_C CODE_LOCATION RhpCheckedAssignRefEBPAVLocation; #endif EXTERN_C CODE_LOCATION RhpByRefAssignRefAVLocation1; -#if !defined(HOST_ARM64) +#if !defined(HOST_ARM64) && !defined(HOST_LOONGARCH64) EXTERN_C CODE_LOCATION RhpByRefAssignRefAVLocation2; #endif @@ -361,7 +328,7 @@ static bool InWriteBarrierHelper(uintptr_t faultingIP) (uintptr_t)&RhpCheckedAssignRefEBPAVLocation, #endif (uintptr_t)&RhpByRefAssignRefAVLocation1, -#if !defined(HOST_ARM64) +#if !defined(HOST_ARM64) && !defined(HOST_LOONGARCH64) (uintptr_t)&RhpByRefAssignRefAVLocation2, #endif }; @@ -443,6 +410,8 @@ static uintptr_t UnwindSimpleHelperToCaller( pContext->SetSp(sp+sizeof(uintptr_t)); // pop the stack #elif defined(HOST_ARM) || defined(HOST_ARM64) uintptr_t adjustedFaultingIP = pContext->GetLr(); +#elif defined(HOST_LOONGARCH64) + uintptr_t adjustedFaultingIP = pContext->GetRa(); #else uintptr_t adjustedFaultingIP = 0; // initializing to make the compiler happy PORTABILITY_ASSERT("UnwindSimpleHelperToCaller"); diff --git a/src/coreclr/nativeaot/Runtime/ICodeManager.h b/src/coreclr/nativeaot/Runtime/ICodeManager.h index dfc6e9efa915a..d1dbd47e51985 100644 --- a/src/coreclr/nativeaot/Runtime/ICodeManager.h +++ b/src/coreclr/nativeaot/Runtime/ICodeManager.h @@ -65,6 +65,27 @@ inline GCRefKind TransitionFrameFlagsToReturnKind(uint64_t transFrameFlags) return returnKind; } +#elif defined(TARGET_LOONGARCH64) +// Verify that we can use bitwise shifts to convert from GCRefKind to PInvokeTransitionFrameFlags and back +C_ASSERT(PTFF_R4_IS_GCREF == ((uint64_t)GCRK_Object << 32)); +C_ASSERT(PTFF_R4_IS_BYREF == ((uint64_t)GCRK_Byref << 32)); +C_ASSERT(PTFF_R5_IS_GCREF == ((uint64_t)GCRK_Scalar_Obj << 32)); +C_ASSERT(PTFF_R5_IS_BYREF == ((uint64_t)GCRK_Scalar_Byref << 32)); + +inline uint64_t ReturnKindToTransitionFrameFlags(GCRefKind returnKind) +{ + // just need to report gc ref bits here. + // appropriate PTFF_SAVE_ bits will be added by the frame building routine. + return ((uint64_t)returnKind << 32); +} + +inline GCRefKind TransitionFrameFlagsToReturnKind(uint64_t transFrameFlags) +{ + GCRefKind returnKind = (GCRefKind)((transFrameFlags & (PTFF_R4_IS_GCREF | PTFF_R4_IS_BYREF | PTFF_R5_IS_GCREF | PTFF_R5_IS_BYREF)) >> 32); + ASSERT((returnKind == GCRK_Scalar) || ((transFrameFlags & PTFF_SAVE_R4) && (transFrameFlags & PTFF_SAVE_R5))); + return returnKind; +} + #elif defined(TARGET_AMD64) // Verify that we can use bitwise shifts to convert from GCRefKind to PInvokeTransitionFrameFlags and back diff --git a/src/coreclr/nativeaot/Runtime/MiscHelpers.cpp b/src/coreclr/nativeaot/Runtime/MiscHelpers.cpp index c5bbcc2284277..3351326ad3071 100644 --- a/src/coreclr/nativeaot/Runtime/MiscHelpers.cpp +++ b/src/coreclr/nativeaot/Runtime/MiscHelpers.cpp @@ -334,6 +334,41 @@ FCIMPL1(uint8_t *, RhGetCodeTarget, uint8_t * pCodeOrg) int64_t distToTarget = ((int64_t)pCode[0] << 38) >> 36; return (uint8_t *)pCode + distToTarget; } +#elif TARGET_LOONGARCH64 + uint32_t * pCode = (uint32_t *)pCodeOrg; + // is this "addi.d $a0, $a0, 8"? + if (pCode[0] == 0x02c02084) + { + // unboxing sequence + unboxingStub = true; + pCode++; + } + // is this an indirect jump? + // pcalau12i $t7, imm20; ld.d $t7, $t7, imm12; jirl $r0, $t7, 0 + if ((pCode[0] & 0xfe000000) == 0x1a000000 && + (pCode[1] & 0xffc00000) == 0x28c00000 && + (pCode[2] & 0xfc000000) == 0x4c000000) + { + // normal import stub - dist to IAT cell is relative to (PC & ~0xfff) + // pcalau12i: imm = SignExtend(imm20:Zeros(12), 64); + int64_t distToIatCell = ((((int64_t)pCode[0] & ~0x1f) << 39) >> 32); + // ld.d: offset = SignExtend(imm12, 64); + distToIatCell += (((int64_t)pCode[1] << 42) >> 52); + uint8_t ** pIatCell = (uint8_t **)(((int64_t)pCode & ~0xfff) + distToIatCell); + return *pIatCell; + } + // is this an unboxing stub followed by a relative jump? + // pcaddu18i $r21, imm20; jirl $r0, $r21, imm16 + else if (unboxingStub && + (pCode[0] & 0xfe00001f) == 0x1e000015 && + (pCode[1] & 0xfc0003ff) == 0x4c0002a0) + { + // relative jump - dist is relative to the instruction + // offset = SignExtend(immhi20:immlo16:'00', 64); + int64_t distToTarget = ((((int64_t)pCode[0] & ~0x1f) << 39) >> 26); + distToTarget += ((((int64_t)pCode[1] & ~0x3ff) << 38) >> 46); + return (uint8_t *)((int64_t)pCode + distToTarget); + } #else UNREFERENCED_PARAMETER(unboxingStub); PORTABILITY_ASSERT("RhGetCodeTarget"); diff --git a/src/coreclr/nativeaot/Runtime/PalRedhawk.h b/src/coreclr/nativeaot/Runtime/PalRedhawk.h index 9257324bd1589..16b719a6cb43c 100644 --- a/src/coreclr/nativeaot/Runtime/PalRedhawk.h +++ b/src/coreclr/nativeaot/Runtime/PalRedhawk.h @@ -448,6 +448,88 @@ typedef struct DECLSPEC_ALIGN(16) _CONTEXT { } } CONTEXT, *PCONTEXT; +#elif defined(HOST_LOONGARCH64) + +#define CONTEXT_LOONGARCH64 0x00800000L + +#define CONTEXT_CONTROL (CONTEXT_LOONGARCH64 | 0x1L) +#define CONTEXT_INTEGER (CONTEXT_LOONGARCH64 | 0x2L) + +// Specify the number of breakpoints and watchpoints that the OS +// will track. Architecturally, LOONGARCH64 supports up to 16. In practice, +// however, almost no one implements more than 4 of each. + +#define LOONGARCH64_MAX_BREAKPOINTS 8 +#define LOONGARCH64_MAX_WATCHPOINTS 2 + +typedef struct DECLSPEC_ALIGN(16) _CONTEXT { + // + // Control flags. + // + uint32_t ContextFlags; + + // + // Integer registers + // + uint64_t R0; + uint64_t Ra; + uint64_t R2; + uint64_t Sp; + uint64_t R4; + uint64_t R5; + uint64_t R6; + uint64_t R7; + uint64_t R8; + uint64_t R9; + uint64_t R10; + uint64_t R11; + uint64_t R12; + uint64_t R13; + uint64_t R14; + uint64_t R15; + uint64_t R16; + uint64_t R17; + uint64_t R18; + uint64_t R19; + uint64_t R20; + uint64_t R21; + uint64_t Fp; + uint64_t R23; + uint64_t R24; + uint64_t R25; + uint64_t R26; + uint64_t R27; + uint64_t R28; + uint64_t R29; + uint64_t R30; + uint64_t R31; + uint64_t Pc; + + // + // Floating Point Registers: FPR64/LSX/LASX. + // + uint64_t F[4*32]; + uint64_t Fcc; + uint32_t Fcsr; + + void SetIp(uintptr_t ip) { Pc = ip; } + void SetArg0Reg(uintptr_t val) { R4 = val; } + void SetArg1Reg(uintptr_t val) { R5 = val; } + uintptr_t GetIp() { return Pc; } + uintptr_t GetRa() { return Ra; } + uintptr_t GetSp() { return Sp; } + + template + void ForEachPossibleObjectRef(F lambda) + { + for (uint64_t* pReg = &R0; pReg <= &R31; pReg++) + lambda((size_t*)pReg); + + // Ra can be used as a scratch register + lambda((size_t*)&Ra); + } +} CONTEXT, *PCONTEXT; + #elif defined(HOST_WASM) typedef struct DECLSPEC_ALIGN(8) _CONTEXT { diff --git a/src/coreclr/nativeaot/Runtime/PalRedhawkCommon.h b/src/coreclr/nativeaot/Runtime/PalRedhawkCommon.h index 29896b7f53ba8..d72b9d9f8e3d7 100644 --- a/src/coreclr/nativeaot/Runtime/PalRedhawkCommon.h +++ b/src/coreclr/nativeaot/Runtime/PalRedhawkCommon.h @@ -91,6 +91,37 @@ struct PAL_LIMITED_CONTEXT uintptr_t GetLr() const { return LR; } void SetIp(uintptr_t ip) { IP = ip; } void SetSp(uintptr_t sp) { SP = sp; } +#elif defined(TARGET_LOONGARCH64) + uintptr_t FP; + uintptr_t RA; + + uintptr_t R4; + uintptr_t R5; + uintptr_t R23; + uintptr_t R24; + uintptr_t R25; + uintptr_t R26; + uintptr_t R27; + uintptr_t R28; + uintptr_t R29; + uintptr_t R30; + uintptr_t R31; + uintptr_t R2; + + uintptr_t SP; + uintptr_t IP; + + uint64_t F[32 - 24]; // Only the F registers F24..F31 need to be preserved + // (F0-F23 are not preserved according to the ABI spec). + + + uintptr_t GetIp() const { return IP; } + uintptr_t GetSp() const { return SP; } + uintptr_t GetFp() const { return FP; } + uintptr_t GetRa() const { return RA; } + void SetIp(uintptr_t ip) { IP = ip; } + void SetSp(uintptr_t sp) { SP = sp; } + #elif defined(UNIX_AMD64_ABI) // Param regs: rdi, rsi, rdx, rcx, r8, r9, scratch: rax, rdx (both return val), preserved: rbp, rbx, r12-r15 uintptr_t IP; diff --git a/src/coreclr/nativeaot/Runtime/StackFrameIterator.cpp b/src/coreclr/nativeaot/Runtime/StackFrameIterator.cpp index ae073e57c7ecd..5155fe0b1e3a4 100644 --- a/src/coreclr/nativeaot/Runtime/StackFrameIterator.cpp +++ b/src/coreclr/nativeaot/Runtime/StackFrameIterator.cpp @@ -239,6 +239,54 @@ void StackFrameIterator::InternalInit(Thread * pThreadToWalk, PInvokeTransitionF m_HijackedReturnValueKind = retValueKind; } +#elif defined(TARGET_LOONGARCH64) + m_RegDisplay.pFP = (PTR_uintptr_t)PTR_HOST_MEMBER_TADDR(PInvokeTransitionFrame, pFrame, m_FramePointer); + m_RegDisplay.pRA = (PTR_uintptr_t)PTR_HOST_MEMBER_TADDR(PInvokeTransitionFrame, pFrame, m_RIP); + + ASSERT(!(pFrame->m_Flags & PTFF_SAVE_FP)); // FP should never contain a GC ref + + if (pFrame->m_Flags & PTFF_SAVE_R23) { m_RegDisplay.pR23 = pPreservedRegsCursor++; } + if (pFrame->m_Flags & PTFF_SAVE_R24) { m_RegDisplay.pR24 = pPreservedRegsCursor++; } + if (pFrame->m_Flags & PTFF_SAVE_R25) { m_RegDisplay.pR25 = pPreservedRegsCursor++; } + if (pFrame->m_Flags & PTFF_SAVE_R26) { m_RegDisplay.pR26 = pPreservedRegsCursor++; } + if (pFrame->m_Flags & PTFF_SAVE_R27) { m_RegDisplay.pR27 = pPreservedRegsCursor++; } + if (pFrame->m_Flags & PTFF_SAVE_R28) { m_RegDisplay.pR28 = pPreservedRegsCursor++; } + if (pFrame->m_Flags & PTFF_SAVE_R29) { m_RegDisplay.pR29 = pPreservedRegsCursor++; } + if (pFrame->m_Flags & PTFF_SAVE_R30) { m_RegDisplay.pR30 = pPreservedRegsCursor++; } + if (pFrame->m_Flags & PTFF_SAVE_R31) { m_RegDisplay.pR31 = pPreservedRegsCursor++; } + + if (pFrame->m_Flags & PTFF_SAVE_SP) { m_RegDisplay.SP = *pPreservedRegsCursor++; } + + if (pFrame->m_Flags & PTFF_SAVE_R0) { m_RegDisplay.pR0 = pPreservedRegsCursor++; } + if (pFrame->m_Flags & PTFF_SAVE_RA) { m_RegDisplay.pRA = pPreservedRegsCursor++; } + if (pFrame->m_Flags & PTFF_SAVE_R2) { m_RegDisplay.pR2 = pPreservedRegsCursor++; } + + if (pFrame->m_Flags & PTFF_SAVE_R4) { m_RegDisplay.pR4 = pPreservedRegsCursor++; } + if (pFrame->m_Flags & PTFF_SAVE_R5) { m_RegDisplay.pR5 = pPreservedRegsCursor++; } + if (pFrame->m_Flags & PTFF_SAVE_R6) { m_RegDisplay.pR6 = pPreservedRegsCursor++; } + if (pFrame->m_Flags & PTFF_SAVE_R7) { m_RegDisplay.pR7 = pPreservedRegsCursor++; } + if (pFrame->m_Flags & PTFF_SAVE_R8) { m_RegDisplay.pR8 = pPreservedRegsCursor++; } + if (pFrame->m_Flags & PTFF_SAVE_R9) { m_RegDisplay.pR9 = pPreservedRegsCursor++; } + if (pFrame->m_Flags & PTFF_SAVE_R10) { m_RegDisplay.pR10 = pPreservedRegsCursor++; } + if (pFrame->m_Flags & PTFF_SAVE_R11) { m_RegDisplay.pR11 = pPreservedRegsCursor++; } + if (pFrame->m_Flags & PTFF_SAVE_R12) { m_RegDisplay.pR12 = pPreservedRegsCursor++; } + if (pFrame->m_Flags & PTFF_SAVE_R13) { m_RegDisplay.pR13 = pPreservedRegsCursor++; } + if (pFrame->m_Flags & PTFF_SAVE_R14) { m_RegDisplay.pR14 = pPreservedRegsCursor++; } + if (pFrame->m_Flags & PTFF_SAVE_R15) { m_RegDisplay.pR15 = pPreservedRegsCursor++; } + if (pFrame->m_Flags & PTFF_SAVE_R16) { m_RegDisplay.pR16 = pPreservedRegsCursor++; } + if (pFrame->m_Flags & PTFF_SAVE_R17) { m_RegDisplay.pR17 = pPreservedRegsCursor++; } + if (pFrame->m_Flags & PTFF_SAVE_R18) { m_RegDisplay.pR18 = pPreservedRegsCursor++; } + if (pFrame->m_Flags & PTFF_SAVE_R19) { m_RegDisplay.pR19 = pPreservedRegsCursor++; } + if (pFrame->m_Flags & PTFF_SAVE_R20) { m_RegDisplay.pR20 = pPreservedRegsCursor++; } + if (pFrame->m_Flags & PTFF_SAVE_R21) { m_RegDisplay.pR21 = pPreservedRegsCursor++; } + + GCRefKind retValueKind = TransitionFrameFlagsToReturnKind(pFrame->m_Flags); + if (retValueKind != GCRK_Scalar) + { + m_pHijackedReturnValue = (PTR_OBJECTREF)m_RegDisplay.pR4; + m_HijackedReturnValueKind = retValueKind; + } + #else // TARGET_ARM if (pFrame->m_Flags & PTFF_SAVE_RBX) { m_RegDisplay.pRbx = pPreservedRegsCursor++; } if (pFrame->m_Flags & PTFF_SAVE_RSI) { m_RegDisplay.pRsi = pPreservedRegsCursor++; } @@ -423,6 +471,35 @@ void StackFrameIterator::InternalInit(Thread * pThreadToWalk, PTR_PAL_LIMITED_CO m_RegDisplay.pX1 = (PTR_uintptr_t)PTR_TO_MEMBER_TADDR(PAL_LIMITED_CONTEXT, pCtx, X1); // TODO: Copy X2-X7 when we start supporting HVA's +#elif defined(TARGET_LOONGARCH64) + // + // preserved regs + // + m_RegDisplay.pR23 = (PTR_uintptr_t)PTR_TO_MEMBER_TADDR(PAL_LIMITED_CONTEXT, pCtx, R23); + m_RegDisplay.pR24 = (PTR_uintptr_t)PTR_TO_MEMBER_TADDR(PAL_LIMITED_CONTEXT, pCtx, R24); + m_RegDisplay.pR25 = (PTR_uintptr_t)PTR_TO_MEMBER_TADDR(PAL_LIMITED_CONTEXT, pCtx, R25); + m_RegDisplay.pR26 = (PTR_uintptr_t)PTR_TO_MEMBER_TADDR(PAL_LIMITED_CONTEXT, pCtx, R26); + m_RegDisplay.pR27 = (PTR_uintptr_t)PTR_TO_MEMBER_TADDR(PAL_LIMITED_CONTEXT, pCtx, R27); + m_RegDisplay.pR28 = (PTR_uintptr_t)PTR_TO_MEMBER_TADDR(PAL_LIMITED_CONTEXT, pCtx, R28); + m_RegDisplay.pR29 = (PTR_uintptr_t)PTR_TO_MEMBER_TADDR(PAL_LIMITED_CONTEXT, pCtx, R29); + m_RegDisplay.pR30 = (PTR_uintptr_t)PTR_TO_MEMBER_TADDR(PAL_LIMITED_CONTEXT, pCtx, R30); + m_RegDisplay.pR31 = (PTR_uintptr_t)PTR_TO_MEMBER_TADDR(PAL_LIMITED_CONTEXT, pCtx, R31); + m_RegDisplay.pFP = (PTR_uintptr_t)PTR_TO_MEMBER_TADDR(PAL_LIMITED_CONTEXT, pCtx, FP); + m_RegDisplay.pRA = (PTR_uintptr_t)PTR_TO_MEMBER_TADDR(PAL_LIMITED_CONTEXT, pCtx, RA); + + // + // preserved vfp regs + // + for (int32_t i = 0; i < 16 - 8; i++) + { + m_RegDisplay.F[i] = pCtx->F[i]; + } + // + // scratch regs + // + m_RegDisplay.pR4 = (PTR_uintptr_t)PTR_TO_MEMBER_TADDR(PAL_LIMITED_CONTEXT, pCtx, R4); + m_RegDisplay.pR5 = (PTR_uintptr_t)PTR_TO_MEMBER_TADDR(PAL_LIMITED_CONTEXT, pCtx, R5); + #elif defined(UNIX_AMD64_ABI) // // preserved regs @@ -611,6 +688,48 @@ void StackFrameIterator::InternalInit(Thread * pThreadToWalk, NATIVE_CONTEXT* pC m_RegDisplay.pR11 = (PTR_uintptr_t)PTR_TO_REG(pCtx, R11); m_RegDisplay.pR12 = (PTR_uintptr_t)PTR_TO_REG(pCtx, R12); m_RegDisplay.pLR = (PTR_uintptr_t)PTR_TO_REG(pCtx, Lr); + +#elif defined(TARGET_LOONGARCH64) + + // + // preserved regs + // + m_RegDisplay.pR23 = (PTR_uintptr_t)PTR_TO_REG(pCtx, R23); + m_RegDisplay.pR24 = (PTR_uintptr_t)PTR_TO_REG(pCtx, R24); + m_RegDisplay.pR25 = (PTR_uintptr_t)PTR_TO_REG(pCtx, R25); + m_RegDisplay.pR26 = (PTR_uintptr_t)PTR_TO_REG(pCtx, R26); + m_RegDisplay.pR27 = (PTR_uintptr_t)PTR_TO_REG(pCtx, R27); + m_RegDisplay.pR28 = (PTR_uintptr_t)PTR_TO_REG(pCtx, R28); + m_RegDisplay.pR29 = (PTR_uintptr_t)PTR_TO_REG(pCtx, R29); + m_RegDisplay.pR30 = (PTR_uintptr_t)PTR_TO_REG(pCtx, R30); + m_RegDisplay.pR31 = (PTR_uintptr_t)PTR_TO_REG(pCtx, R31); + m_RegDisplay.pFP = (PTR_uintptr_t)PTR_TO_REG(pCtx, Fp); + m_RegDisplay.pRA = (PTR_uintptr_t)PTR_TO_REG(pCtx, Ra); + + // + // scratch regs + // + m_RegDisplay.pR0 = (PTR_uintptr_t)PTR_TO_REG(pCtx, R0); + m_RegDisplay.pR2 = (PTR_uintptr_t)PTR_TO_REG(pCtx, R2); + m_RegDisplay.pR4 = (PTR_uintptr_t)PTR_TO_REG(pCtx, R4); + m_RegDisplay.pR5 = (PTR_uintptr_t)PTR_TO_REG(pCtx, R5); + m_RegDisplay.pR6 = (PTR_uintptr_t)PTR_TO_REG(pCtx, R6); + m_RegDisplay.pR7 = (PTR_uintptr_t)PTR_TO_REG(pCtx, R7); + m_RegDisplay.pR8 = (PTR_uintptr_t)PTR_TO_REG(pCtx, R8); + m_RegDisplay.pR9 = (PTR_uintptr_t)PTR_TO_REG(pCtx, R9); + m_RegDisplay.pR10 = (PTR_uintptr_t)PTR_TO_REG(pCtx, R10); + m_RegDisplay.pR11 = (PTR_uintptr_t)PTR_TO_REG(pCtx, R11); + m_RegDisplay.pR12 = (PTR_uintptr_t)PTR_TO_REG(pCtx, R12); + m_RegDisplay.pR13 = (PTR_uintptr_t)PTR_TO_REG(pCtx, R13); + m_RegDisplay.pR14 = (PTR_uintptr_t)PTR_TO_REG(pCtx, R14); + m_RegDisplay.pR15 = (PTR_uintptr_t)PTR_TO_REG(pCtx, R15); + m_RegDisplay.pR16 = (PTR_uintptr_t)PTR_TO_REG(pCtx, R16); + m_RegDisplay.pR17 = (PTR_uintptr_t)PTR_TO_REG(pCtx, R17); + m_RegDisplay.pR18 = (PTR_uintptr_t)PTR_TO_REG(pCtx, R18); + m_RegDisplay.pR19 = (PTR_uintptr_t)PTR_TO_REG(pCtx, R19); + m_RegDisplay.pR20 = (PTR_uintptr_t)PTR_TO_REG(pCtx, R20); + m_RegDisplay.pR21 = (PTR_uintptr_t)PTR_TO_REG(pCtx, R21); + #else PORTABILITY_ASSERT("StackFrameIterator::InternalInit"); #endif // TARGET_ARM @@ -731,6 +850,18 @@ void StackFrameIterator::UpdateFromExceptionDispatch(PTR_StackFrameIterator pSou m_RegDisplay.pX28 = thisFuncletPtrs.pX28; m_RegDisplay.pFP = thisFuncletPtrs.pFP; +#elif defined(TARGET_LOONGARCH64) + m_RegDisplay.pR23 = thisFuncletPtrs.pR23; + m_RegDisplay.pR24 = thisFuncletPtrs.pR24; + m_RegDisplay.pR25 = thisFuncletPtrs.pR25; + m_RegDisplay.pR26 = thisFuncletPtrs.pR26; + m_RegDisplay.pR27 = thisFuncletPtrs.pR27; + m_RegDisplay.pR28 = thisFuncletPtrs.pR28; + m_RegDisplay.pR29 = thisFuncletPtrs.pR29; + m_RegDisplay.pR30 = thisFuncletPtrs.pR30; + m_RegDisplay.pR31 = thisFuncletPtrs.pR31; + m_RegDisplay.pFP = thisFuncletPtrs.pFP; + #elif defined(UNIX_AMD64_ABI) // Save the preserved regs portion of the REGDISPLAY across the unwind through the C# EH dispatch code. m_RegDisplay.pRbp = thisFuncletPtrs.pRbp; @@ -964,12 +1095,54 @@ void StackFrameIterator::UnwindFuncletInvokeThunk() m_RegDisplay.pX27 = SP++; m_RegDisplay.pX28 = SP++; +#elif defined(TARGET_LOONGARCH64) + PTR_uint64_t f = (PTR_uint64_t)(m_RegDisplay.SP); + + for (int i = 0; i < 8; i++) + { + m_RegDisplay.F[i] = *f++; + } + + SP = (PTR_uintptr_t)f; + + if (!isFilterInvoke) + { + // RhpCallCatchFunclet puts a couple of extra things on the stack that aren't put there by the other two + // thunks, but we don't need to know what they are here, so we just skip them. + SP += EQUALS_RETURN_ADDRESS(m_ControlPC, RhpCallCatchFunclet2) ? 6 : 4; + // Save the preserved regs portion of the REGDISPLAY across the unwind through the C# EH dispatch code. + m_funcletPtrs.pR23 = m_RegDisplay.pR23; + m_funcletPtrs.pR24 = m_RegDisplay.pR24; + m_funcletPtrs.pR25 = m_RegDisplay.pR25; + m_funcletPtrs.pR26 = m_RegDisplay.pR26; + m_funcletPtrs.pR27 = m_RegDisplay.pR27; + m_funcletPtrs.pR28 = m_RegDisplay.pR28; + m_funcletPtrs.pR29 = m_RegDisplay.pR29; + m_funcletPtrs.pR30 = m_RegDisplay.pR30; + m_funcletPtrs.pR31 = m_RegDisplay.pR31; + m_funcletPtrs.pFP = m_RegDisplay.pFP; + } + + m_RegDisplay.pFP = SP++; + + m_RegDisplay.SetIP(*SP++); + + m_RegDisplay.pR23 = SP++; + m_RegDisplay.pR24 = SP++; + m_RegDisplay.pR25 = SP++; + m_RegDisplay.pR26 = SP++; + m_RegDisplay.pR27 = SP++; + m_RegDisplay.pR28 = SP++; + m_RegDisplay.pR29 = SP++; + m_RegDisplay.pR30 = SP++; + m_RegDisplay.pR31 = SP++; + #else SP = (PTR_uintptr_t)(m_RegDisplay.SP); ASSERT_UNCONDITIONALLY("NYI for this arch"); #endif -#if !defined(TARGET_ARM64) +#if !defined(TARGET_ARM64) && !defined(TARGET_LOONGARCH64) m_RegDisplay.SetIP(PCODEToPINSTR(*SP++)); #endif @@ -1104,6 +1277,30 @@ struct UniversalTransitionStackFrame { pRegisterSet->pFP = GET_POINTER_TO_FIELD(m_pushedFP); } + +#elif defined(TARGET_LOONGARCH64) + + // Conservative GC reporting must be applied to everything between the base of the + // ReturnBlock and the top of the StackPassedArgs. +private: + uintptr_t m_pushedFP; // ChildSP+000 CallerSP-100 (0x08 bytes) (fp) + uintptr_t m_pushedRA; // ChildSP+008 CallerSP-0F8 (0x08 bytes) (ra) + Fp128 m_fpArgRegs[8]; // ChildSP+010 CallerSP-0F0 (0x80 bytes) (q0-q7) + uintptr_t m_returnBlock[4]; // ChildSP+090 CallerSP-070 (0x40 bytes) + uintptr_t m_intArgRegs[9]; // ChildSP+0B0 CallerSP-050 (0x48 bytes) (x0-x8) + uintptr_t m_alignmentPad; // ChildSP+0F8 CallerSP-008 (0x08 bytes) + uintptr_t m_stackPassedArgs[1]; // ChildSP+100 CallerSP+000 (unknown size) + +public: + PTR_uintptr_t get_CallerSP() { return GET_POINTER_TO_FIELD(m_stackPassedArgs[0]); } + PTR_uintptr_t get_AddressOfPushedCallerIP() { return GET_POINTER_TO_FIELD(m_pushedRA); } + PTR_uintptr_t get_LowerBoundForConservativeReporting() { return GET_POINTER_TO_FIELD(m_returnBlock[0]); } + + void UnwindNonVolatileRegisters(REGDISPLAY * pRegisterSet) + { + pRegisterSet->pFP = GET_POINTER_TO_FIELD(m_pushedFP); + } + #elif defined(TARGET_WASM) private: // WASMTODO: #error NYI for this arch @@ -1175,6 +1372,8 @@ void StackFrameIterator::UnwindUniversalTransitionThunk() #define STACK_ALIGN_SIZE 16 #elif defined(TARGET_X86) #define STACK_ALIGN_SIZE 4 +#elif defined(TARGET_LOONGARCH64) +#define STACK_ALIGN_SIZE 16 #elif defined(TARGET_WASM) #define STACK_ALIGN_SIZE 4 #endif @@ -1240,6 +1439,17 @@ void StackFrameIterator::UnwindThrowSiteThunk() m_RegDisplay.pRdi = (PTR_uintptr_t)PTR_TO_MEMBER_TADDR(PAL_LIMITED_CONTEXT, pContext, Rdi); m_RegDisplay.pRsi = (PTR_uintptr_t)PTR_TO_MEMBER_TADDR(PAL_LIMITED_CONTEXT, pContext, Rsi); m_RegDisplay.pRbx = (PTR_uintptr_t)PTR_TO_MEMBER_TADDR(PAL_LIMITED_CONTEXT, pContext, Rbx); +#elif defined(TARGET_LOONGARCH64) + m_RegDisplay.pR23 = (PTR_uintptr_t)PTR_TO_MEMBER_TADDR(PAL_LIMITED_CONTEXT, pContext, R23); + m_RegDisplay.pR24 = (PTR_uintptr_t)PTR_TO_MEMBER_TADDR(PAL_LIMITED_CONTEXT, pContext, R24); + m_RegDisplay.pR25 = (PTR_uintptr_t)PTR_TO_MEMBER_TADDR(PAL_LIMITED_CONTEXT, pContext, R25); + m_RegDisplay.pR26 = (PTR_uintptr_t)PTR_TO_MEMBER_TADDR(PAL_LIMITED_CONTEXT, pContext, R26); + m_RegDisplay.pR27 = (PTR_uintptr_t)PTR_TO_MEMBER_TADDR(PAL_LIMITED_CONTEXT, pContext, R27); + m_RegDisplay.pR28 = (PTR_uintptr_t)PTR_TO_MEMBER_TADDR(PAL_LIMITED_CONTEXT, pContext, R28); + m_RegDisplay.pR29 = (PTR_uintptr_t)PTR_TO_MEMBER_TADDR(PAL_LIMITED_CONTEXT, pContext, R29); + m_RegDisplay.pR30 = (PTR_uintptr_t)PTR_TO_MEMBER_TADDR(PAL_LIMITED_CONTEXT, pContext, R30); + m_RegDisplay.pR31 = (PTR_uintptr_t)PTR_TO_MEMBER_TADDR(PAL_LIMITED_CONTEXT, pContext, R31); + m_RegDisplay.pFP = (PTR_uintptr_t)PTR_TO_MEMBER_TADDR(PAL_LIMITED_CONTEXT, pContext, FP); #else ASSERT_UNCONDITIONALLY("NYI for this arch"); #endif diff --git a/src/coreclr/nativeaot/Runtime/StackFrameIterator.h b/src/coreclr/nativeaot/Runtime/StackFrameIterator.h index cf7f524de8dbb..7eb6351f4dbe3 100644 --- a/src/coreclr/nativeaot/Runtime/StackFrameIterator.h +++ b/src/coreclr/nativeaot/Runtime/StackFrameIterator.h @@ -179,6 +179,17 @@ class StackFrameIterator PTR_uintptr_t pX27; PTR_uintptr_t pX28; PTR_uintptr_t pFP; +#elif defined(TARGET_LOONGARCH64) + PTR_uintptr_t pR23; + PTR_uintptr_t pR24; + PTR_uintptr_t pR25; + PTR_uintptr_t pR26; + PTR_uintptr_t pR27; + PTR_uintptr_t pR28; + PTR_uintptr_t pR29; + PTR_uintptr_t pR30; + PTR_uintptr_t pR31; + PTR_uintptr_t pFP; #elif defined(UNIX_AMD64_ABI) PTR_uintptr_t pRbp; PTR_uintptr_t pRbx; diff --git a/src/coreclr/nativeaot/Runtime/ThunksMapping.cpp b/src/coreclr/nativeaot/Runtime/ThunksMapping.cpp index d22f30e19d9e0..c8f91a07a2819 100644 --- a/src/coreclr/nativeaot/Runtime/ThunksMapping.cpp +++ b/src/coreclr/nativeaot/Runtime/ThunksMapping.cpp @@ -22,6 +22,8 @@ #define THUNK_SIZE 20 #elif TARGET_ARM64 #define THUNK_SIZE 16 +#elif TARGET_LOONGARCH64 +#define THUNK_SIZE 16 #else #define THUNK_SIZE (2 * OS_PAGE_SIZE) // This will cause RhpGetNumThunksPerBlock to return 0 #endif @@ -231,6 +233,28 @@ EXTERN_C void* QCALLTYPE RhAllocateThunksMapping() *((uint32_t*)pCurrentThunkAddress) = 0xD43E0000; pCurrentThunkAddress += 4; + +#elif TARGET_LOONGARCH64 + + //pcaddi $t7, + //pcaddi $t8, - + //ld.d $t8, $t8, + //jirl $r0, $t8, 0 + + int delta = (int)(pCurrentDataAddress - pCurrentThunkAddress); + *((uint32_t*)pCurrentThunkAddress) = 0x18000013 | (((delta & 0x3FFFFC) >> 2) << 5); + pCurrentThunkAddress += 4; + + delta += OS_PAGE_SIZE - POINTER_SIZE - (i * POINTER_SIZE * 2) - 4; + *((uint32_t*)pCurrentThunkAddress) = 0x18000014 | (((delta & 0x3FFFFC) >> 2) << 5); + pCurrentThunkAddress += 4; + + *((uint32_t*)pCurrentThunkAddress) = 0x28C00294; + pCurrentThunkAddress += 4; + + *((uint32_t*)pCurrentThunkAddress) = 0x4C000280; + pCurrentThunkAddress += 4; + #else UNREFERENCED_PARAMETER(pCurrentDataAddress); UNREFERENCED_PARAMETER(pCurrentThunkAddress); diff --git a/src/coreclr/nativeaot/Runtime/gcenv.ee.cpp b/src/coreclr/nativeaot/Runtime/gcenv.ee.cpp index 2cb9445144c22..f041e499c11d4 100644 --- a/src/coreclr/nativeaot/Runtime/gcenv.ee.cpp +++ b/src/coreclr/nativeaot/Runtime/gcenv.ee.cpp @@ -56,7 +56,7 @@ void GCToEEInterface::RestartEE(bool /*bFinishedGC*/) { FireEtwGCRestartEEBegin_V1(GetClrInstanceId()); -#if defined(TARGET_ARM) || defined(TARGET_ARM64) +#if !defined(TARGET_X86) && !defined(TARGET_AMD64) // Flush the store buffers on all CPUs, to ensure that they all see changes made // by the GC threads. This only matters on weak memory ordered processors as // the strong memory ordered processors wouldn't have reordered the relevant reads. @@ -64,7 +64,7 @@ void GCToEEInterface::RestartEE(bool /*bFinishedGC*/) // the runtime was suspended and that will return to cooperative mode after the runtime // is restarted. ::FlushProcessWriteBuffers(); -#endif //TARGET_ARM || TARGET_ARM64 +#endif // !defined(TARGET_X86) && !defined(TARGET_AMD64) SyncClean::CleanUp(); diff --git a/src/coreclr/nativeaot/Runtime/inc/TargetPtrs.h b/src/coreclr/nativeaot/Runtime/inc/TargetPtrs.h index 0ef7f5e8a84f7..ece8ae50b379e 100644 --- a/src/coreclr/nativeaot/Runtime/inc/TargetPtrs.h +++ b/src/coreclr/nativeaot/Runtime/inc/TargetPtrs.h @@ -13,6 +13,8 @@ typedef uint32_t UIntTarget; typedef uint64_t UIntTarget; #elif defined(TARGET_WASM) typedef uint32_t UIntTarget; +#elif defined(TARGET_LOONGARCH64) +typedef uint64_t UIntTarget; #else #error unexpected target architecture #endif diff --git a/src/coreclr/nativeaot/Runtime/inc/rhbinder.h b/src/coreclr/nativeaot/Runtime/inc/rhbinder.h index f0ebc5b7a7e50..f72ff28caf001 100644 --- a/src/coreclr/nativeaot/Runtime/inc/rhbinder.h +++ b/src/coreclr/nativeaot/Runtime/inc/rhbinder.h @@ -338,6 +338,67 @@ enum PInvokeTransitionFrameFlags : uint64_t PTFF_THREAD_ABORT = 0x0000001000000000, // indicates that ThreadAbortException should be thrown when returning from the transition }; +#elif defined(TARGET_LOONGARCH64) +enum PInvokeTransitionFrameFlags : uint64_t +{ + // NOTE: Keep in sync with src\coreclr\nativeaot\Runtime\loongarch64\AsmMacros.h + + // NOTE: The order in which registers get pushed in the PInvokeTransitionFrame's m_PreservedRegs list has + // to match the order of these flags (that's also the order in which they are read in StackFrameIterator.cpp + + // standard preserved registers + PTFF_SAVE_R23 = 0x0000000000000001, + PTFF_SAVE_R24 = 0x0000000000000002, + PTFF_SAVE_R25 = 0x0000000000000004, + PTFF_SAVE_R26 = 0x0000000000000008, + PTFF_SAVE_R27 = 0x0000000000000010, + PTFF_SAVE_R28 = 0x0000000000000020, + PTFF_SAVE_R29 = 0x0000000000000040, + PTFF_SAVE_R30 = 0x0000000000000080, + PTFF_SAVE_R31 = 0x0000000000000100, + + PTFF_SAVE_SP = 0x0000000000000200, // Used for 'coop pinvokes' in runtime helper routines. Methods with + // PInvokes are required to have a frame pointers, but methods which + // call runtime helpers are not. Therefore, methods that call runtime + // helpers may need SP to seed the stackwalk. + + // Scratch registers + PTFF_SAVE_R0 = 0x0000000000000400, + PTFF_SAVE_R2 = 0x0000000000000800, + PTFF_SAVE_R4 = 0x0000000000001000, + PTFF_SAVE_R5 = 0x0000000000002000, + PTFF_SAVE_R6 = 0x0000000000004000, + PTFF_SAVE_R7 = 0x0000000000008000, + PTFF_SAVE_R8 = 0x0000000000010000, + PTFF_SAVE_R9 = 0x0000000000020000, + PTFF_SAVE_R10 = 0x0000000000040000, + PTFF_SAVE_R11 = 0x0000000000080000, + PTFF_SAVE_R12 = 0x0000000000100000, + PTFF_SAVE_R13 = 0x0000000000200000, + PTFF_SAVE_R14 = 0x0000000000400000, + PTFF_SAVE_R15 = 0x0000000000800000, + PTFF_SAVE_R16 = 0x0000000001000000, + PTFF_SAVE_R17 = 0x0000000002000000, + PTFF_SAVE_R18 = 0x0000000004000000, + PTFF_SAVE_R19 = 0x0000000008000000, + PTFF_SAVE_R20 = 0x0000000010000000, + PTFF_SAVE_R21 = 0x0000000020000000, + + PTFF_SAVE_FP = 0x0000000040000000, // should never be used, we require FP frames for methods with + // pinvoke and it is saved into the frame pointer field instead + + PTFF_SAVE_RA = 0x0000000080000000, // this is useful for the case of loop hijacking where we need both + // a return address pointing into the hijacked method and that method's + // ra register, which may hold a gc pointer + + // used by hijack handler to report return value of hijacked method + PTFF_R4_IS_GCREF = 0x0000000100000000, + PTFF_R4_IS_BYREF = 0x0000000200000000, + PTFF_R5_IS_GCREF = 0x0000000400000000, + PTFF_R5_IS_BYREF = 0x0000000800000000, + + PTFF_THREAD_ABORT = 0x0000001000000000, // indicates that ThreadAbortException should be thrown when returning from the transition +}; #else // TARGET_ARM enum PInvokeTransitionFrameFlags @@ -412,6 +473,8 @@ struct PInvokeTransitionFrame // can be an invalid pointer in universal transition cases (which never need to call GetThread) #ifdef TARGET_ARM64 uint64_t m_Flags; // PInvokeTransitionFrameFlags +#elif TARGET_LOONGARCH64 + uint64_t m_Flags; // PInvokeTransitionFrameFlags #else uint32_t m_Flags; // PInvokeTransitionFrameFlags #endif @@ -436,6 +499,8 @@ struct PInvokeTransitionFrame #define OFFSETOF__Thread__m_pTransitionFrame 0x40 #elif defined(TARGET_ARM64) #define OFFSETOF__Thread__m_pTransitionFrame 0x40 +#elif defined(TARGET_LOONGARCH64) +#define OFFSETOF__Thread__m_pTransitionFrame 0x40 #elif defined(TARGET_X86) #define OFFSETOF__Thread__m_pTransitionFrame 0x2c #elif defined(TARGET_ARM) diff --git a/src/coreclr/nativeaot/Runtime/regdisplay.h b/src/coreclr/nativeaot/Runtime/regdisplay.h index 739a4eec23090..e95c9d5fd71d6 100644 --- a/src/coreclr/nativeaot/Runtime/regdisplay.h +++ b/src/coreclr/nativeaot/Runtime/regdisplay.h @@ -169,6 +169,62 @@ struct REGDISPLAY inline void SetIP(PCODE IP) { this->IP = IP; } inline void SetSP(uintptr_t SP) { this->SP = SP; } }; + +#elif defined(TARGET_LOONGARCH64) + +struct REGDISPLAY +{ + PTR_uintptr_t pR0; + PTR_uintptr_t pRA; + PTR_uintptr_t pR2; + + uintptr_t SP; + + PTR_uintptr_t pR4; + PTR_uintptr_t pR5; + PTR_uintptr_t pR6; + PTR_uintptr_t pR7; + PTR_uintptr_t pR8; + PTR_uintptr_t pR9; + PTR_uintptr_t pR10; + PTR_uintptr_t pR11; + PTR_uintptr_t pR12; + PTR_uintptr_t pR13; + PTR_uintptr_t pR14; + PTR_uintptr_t pR15; + PTR_uintptr_t pR16; + PTR_uintptr_t pR17; + PTR_uintptr_t pR18; + PTR_uintptr_t pR19; + PTR_uintptr_t pR20; + PTR_uintptr_t pR21; + PTR_uintptr_t pFP; + PTR_uintptr_t pR23; + PTR_uintptr_t pR24; + PTR_uintptr_t pR25; + PTR_uintptr_t pR26; + PTR_uintptr_t pR27; + PTR_uintptr_t pR28; + PTR_uintptr_t pR29; + PTR_uintptr_t pR30; + PTR_uintptr_t pR31; + + PCODE IP; + + uint64_t F[32-24]; // Only the F registers F24..F31 needs to be preserved + // (F0-F23 are not preserved according to the ABI spec). + // These need to be unwound during a stack walk + // for EH, but not adjusted, so we only need + // their values, not their addresses + + inline PCODE GetIP() { return IP; } + inline uintptr_t GetSP() { return SP; } + inline uintptr_t GetFP() { return *pFP; } + + inline void SetIP(PCODE IP) { this->IP = IP; } + inline void SetSP(uintptr_t SP) { this->SP = SP; } +}; + #elif defined(TARGET_WASM) struct REGDISPLAY @@ -185,7 +241,7 @@ struct REGDISPLAY inline void SetIP(PCODE IP) { } inline void SetSP(uintptr_t SP) { } }; -#endif // HOST_X86 || HOST_AMD64 || HOST_ARM || HOST_ARM64 || HOST_WASM +#endif // HOST_X86 || HOST_AMD64 || HOST_ARM || HOST_ARM64 || HOST_WASM || HOST_LOONGARCH64 typedef REGDISPLAY * PREGDISPLAY; diff --git a/src/coreclr/nativeaot/Runtime/unix/UnixContext.cpp b/src/coreclr/nativeaot/Runtime/unix/UnixContext.cpp index 8e587901f60a3..e084fb35e391f 100644 --- a/src/coreclr/nativeaot/Runtime/unix/UnixContext.cpp +++ b/src/coreclr/nativeaot/Runtime/unix/UnixContext.cpp @@ -80,7 +80,43 @@ #if HAVE___GREGSET_T -#ifdef HOST_64BIT +#if defined(HOST_LOONGARCH64) + +#define MCREG_R0(mc) ((mc).__gregs[0]) +#define MCREG_Ra(mc) ((mc).__gregs[1]) +#define MCREG_Tp(mc) ((mc).__gregs[2]) +#define MCREG_Sp(mc) ((mc).__gregs[3]) +#define MCREG_A0(mc) ((mc).__gregs[4]) +#define MCREG_A1(mc) ((mc).__gregs[5]) +#define MCREG_A2(mc) ((mc).__gregs[6]) +#define MCREG_A3(mc) ((mc).__gregs[7]) +#define MCREG_A4(mc) ((mc).__gregs[8]) +#define MCREG_A5(mc) ((mc).__gregs[9]) +#define MCREG_A6(mc) ((mc).__gregs[10]) +#define MCREG_A7(mc) ((mc).__gregs[11]) +#define MCREG_T0(mc) ((mc).__gregs[12]) +#define MCREG_T1(mc) ((mc).__gregs[13]) +#define MCREG_T2(mc) ((mc).__gregs[14]) +#define MCREG_T3(mc) ((mc).__gregs[15]) +#define MCREG_T4(mc) ((mc).__gregs[16]) +#define MCREG_T5(mc) ((mc).__gregs[17]) +#define MCREG_T6(mc) ((mc).__gregs[18]) +#define MCREG_T7(mc) ((mc).__gregs[19]) +#define MCREG_T8(mc) ((mc).__gregs[20]) +#define MCREG_X0(mc) ((mc).__gregs[21]) +#define MCREG_Fp(mc) ((mc).__gregs[22]) +#define MCREG_S0(mc) ((mc).__gregs[23]) +#define MCREG_S1(mc) ((mc).__gregs[24]) +#define MCREG_S2(mc) ((mc).__gregs[25]) +#define MCREG_S3(mc) ((mc).__gregs[26]) +#define MCREG_S4(mc) ((mc).__gregs[27]) +#define MCREG_S5(mc) ((mc).__gregs[28]) +#define MCREG_S6(mc) ((mc).__gregs[29]) +#define MCREG_S7(mc) ((mc).__gregs[30]) +#define MCREG_S8(mc) ((mc).__gregs[31]) +#define MCREG_Pc(mc) ((mc).__pc) + +#elif HOST_64BIT #define MCREG_Rip(mc) ((mc).__gregs[_REG_RIP]) #define MCREG_Rsp(mc) ((mc).__gregs[_REG_RSP]) #define MCREG_Rax(mc) ((mc).__gregs[_REG_RAX]) @@ -115,7 +151,43 @@ #elif HAVE_GREGSET_T -#ifdef HOST_64BIT +#if defined(HOST_LOONGARCH64) + +#define MCREG_R0(mc) ((mc).__gregs[0]) +#define MCREG_Ra(mc) ((mc).__gregs[1]) +#define MCREG_Tp(mc) ((mc).__gregs[2]) +#define MCREG_Sp(mc) ((mc).__gregs[3]) +#define MCREG_A0(mc) ((mc).__gregs[4]) +#define MCREG_A1(mc) ((mc).__gregs[5]) +#define MCREG_A2(mc) ((mc).__gregs[6]) +#define MCREG_A3(mc) ((mc).__gregs[7]) +#define MCREG_A4(mc) ((mc).__gregs[8]) +#define MCREG_A5(mc) ((mc).__gregs[9]) +#define MCREG_A6(mc) ((mc).__gregs[10]) +#define MCREG_A7(mc) ((mc).__gregs[11]) +#define MCREG_T0(mc) ((mc).__gregs[12]) +#define MCREG_T1(mc) ((mc).__gregs[13]) +#define MCREG_T2(mc) ((mc).__gregs[14]) +#define MCREG_T3(mc) ((mc).__gregs[15]) +#define MCREG_T4(mc) ((mc).__gregs[16]) +#define MCREG_T5(mc) ((mc).__gregs[17]) +#define MCREG_T6(mc) ((mc).__gregs[18]) +#define MCREG_T7(mc) ((mc).__gregs[19]) +#define MCREG_T8(mc) ((mc).__gregs[20]) +#define MCREG_X0(mc) ((mc).__gregs[21]) +#define MCREG_Fp(mc) ((mc).__gregs[22]) +#define MCREG_S0(mc) ((mc).__gregs[23]) +#define MCREG_S1(mc) ((mc).__gregs[24]) +#define MCREG_S2(mc) ((mc).__gregs[25]) +#define MCREG_S3(mc) ((mc).__gregs[26]) +#define MCREG_S4(mc) ((mc).__gregs[27]) +#define MCREG_S5(mc) ((mc).__gregs[28]) +#define MCREG_S6(mc) ((mc).__gregs[29]) +#define MCREG_S7(mc) ((mc).__gregs[30]) +#define MCREG_S8(mc) ((mc).__gregs[31]) +#define MCREG_Pc(mc) ((mc).__pc) + +#elif HOST_64BIT #define MCREG_Rip(mc) ((mc).gregs[REG_RIP]) #define MCREG_Rsp(mc) ((mc).gregs[REG_RSP]) #define MCREG_Rax(mc) ((mc).gregs[REG_RAX]) @@ -224,6 +296,42 @@ #define MCREG_Sp(mc) ((mc).sp) #define MCREG_Pc(mc) ((mc).pc) +#elif defined(HOST_LOONGARCH64) + +#define MCREG_R0(mc) ((mc).regs[0]) +#define MCREG_Ra(mc) ((mc).regs[1]) +#define MCREG_Tp(mc) ((mc).regs[2]) +#define MCREG_Sp(mc) ((mc).regs[3]) +#define MCREG_A0(mc) ((mc).regs[4]) +#define MCREG_A1(mc) ((mc).regs[5]) +#define MCREG_A2(mc) ((mc).regs[6]) +#define MCREG_A3(mc) ((mc).regs[7]) +#define MCREG_A4(mc) ((mc).regs[8]) +#define MCREG_A5(mc) ((mc).regs[9]) +#define MCREG_A6(mc) ((mc).regs[10]) +#define MCREG_A7(mc) ((mc).regs[11]) +#define MCREG_T0(mc) ((mc).regs[12]) +#define MCREG_T1(mc) ((mc).regs[13]) +#define MCREG_T2(mc) ((mc).regs[14]) +#define MCREG_T3(mc) ((mc).regs[15]) +#define MCREG_T4(mc) ((mc).regs[16]) +#define MCREG_T5(mc) ((mc).regs[17]) +#define MCREG_T6(mc) ((mc).regs[18]) +#define MCREG_T7(mc) ((mc).regs[19]) +#define MCREG_T8(mc) ((mc).regs[20]) +#define MCREG_X0(mc) ((mc).regs[21]) +#define MCREG_Fp(mc) ((mc).regs[22]) +#define MCREG_S0(mc) ((mc).regs[23]) +#define MCREG_S1(mc) ((mc).regs[24]) +#define MCREG_S2(mc) ((mc).regs[25]) +#define MCREG_S3(mc) ((mc).regs[26]) +#define MCREG_S4(mc) ((mc).regs[27]) +#define MCREG_S5(mc) ((mc).regs[28]) +#define MCREG_S6(mc) ((mc).regs[29]) +#define MCREG_S7(mc) ((mc).regs[30]) +#define MCREG_S8(mc) ((mc).regs[31]) +#define MCREG_Pc(mc) ((mc).pc) + #else // For FreeBSD, as found in x86/ucontext.h @@ -365,6 +473,29 @@ MCREG_X0(nativeContext->uc_mcontext) = arg0Reg; \ MCREG_X1(nativeContext->uc_mcontext) = arg1Reg; +#elif defined(HOST_LOONGARCH64) + +#define ASSIGN_CONTROL_REGS \ + ASSIGN_REG(Pc, IP) \ + ASSIGN_REG(Sp, SP) \ + ASSIGN_REG(Fp, FP) \ + ASSIGN_REG(Ra, RA) + +#define ASSIGN_INTEGER_REGS \ + ASSIGN_REG(S0, R23) \ + ASSIGN_REG(S1, R24) \ + ASSIGN_REG(S2, R25) \ + ASSIGN_REG(S3, R26) \ + ASSIGN_REG(S4, R27) \ + ASSIGN_REG(S5, R28) \ + ASSIGN_REG(S6, R29) \ + ASSIGN_REG(S7, R30) \ + ASSIGN_REG(S8, R31) + +#define ASSIGN_TWO_ARGUMENT_REGS \ + MCREG_A0(nativeContext->uc_mcontext) = arg0Reg; \ + MCREG_A1(nativeContext->uc_mcontext) = arg1Reg; + #elif defined(HOST_WASM) // TODO: determine how unwinding will work on WebAssembly #define ASSIGN_CONTROL_REGS @@ -529,6 +660,42 @@ uint64_t GetPC(void* context) uint64_t& UNIX_CONTEXT::R11(){ return (uint64_t&)MCREG_R11(ctx.uc_mcontext); } uint64_t& UNIX_CONTEXT::R12(){ return (uint64_t&)MCREG_R12(ctx.uc_mcontext); } +#elif TARGET_LOONGARCH64 + + uint64_t& UNIX_CONTEXT::R0() { return (uint64_t&)MCREG_R0(ctx.uc_mcontext); } + uint64_t& UNIX_CONTEXT::R2() { return (uint64_t&)MCREG_Tp(ctx.uc_mcontext); } + uint64_t& UNIX_CONTEXT::R4() { return (uint64_t&)MCREG_A0(ctx.uc_mcontext); } + uint64_t& UNIX_CONTEXT::R5() { return (uint64_t&)MCREG_A1(ctx.uc_mcontext); } + uint64_t& UNIX_CONTEXT::R6() { return (uint64_t&)MCREG_A2(ctx.uc_mcontext); } + uint64_t& UNIX_CONTEXT::R7() { return (uint64_t&)MCREG_A3(ctx.uc_mcontext); } + uint64_t& UNIX_CONTEXT::R8() { return (uint64_t&)MCREG_A4(ctx.uc_mcontext); } + uint64_t& UNIX_CONTEXT::R9() { return (uint64_t&)MCREG_A5(ctx.uc_mcontext); } + uint64_t& UNIX_CONTEXT::R10() { return (uint64_t&)MCREG_A6(ctx.uc_mcontext); } + uint64_t& UNIX_CONTEXT::R11() { return (uint64_t&)MCREG_A7(ctx.uc_mcontext); } + uint64_t& UNIX_CONTEXT::R12() { return (uint64_t&)MCREG_T0(ctx.uc_mcontext); } + uint64_t& UNIX_CONTEXT::R13() { return (uint64_t&)MCREG_T1(ctx.uc_mcontext); } + uint64_t& UNIX_CONTEXT::R14() { return (uint64_t&)MCREG_T2(ctx.uc_mcontext); } + uint64_t& UNIX_CONTEXT::R15() { return (uint64_t&)MCREG_T3(ctx.uc_mcontext); } + uint64_t& UNIX_CONTEXT::R16() { return (uint64_t&)MCREG_T4(ctx.uc_mcontext); } + uint64_t& UNIX_CONTEXT::R17() { return (uint64_t&)MCREG_T5(ctx.uc_mcontext); } + uint64_t& UNIX_CONTEXT::R18() { return (uint64_t&)MCREG_T6(ctx.uc_mcontext); } + uint64_t& UNIX_CONTEXT::R19() { return (uint64_t&)MCREG_T7(ctx.uc_mcontext); } + uint64_t& UNIX_CONTEXT::R20() { return (uint64_t&)MCREG_T8(ctx.uc_mcontext); } + uint64_t& UNIX_CONTEXT::R21() { return (uint64_t&)MCREG_X0(ctx.uc_mcontext); } + uint64_t& UNIX_CONTEXT::R23() { return (uint64_t&)MCREG_S0(ctx.uc_mcontext); } + uint64_t& UNIX_CONTEXT::R24() { return (uint64_t&)MCREG_S1(ctx.uc_mcontext); } + uint64_t& UNIX_CONTEXT::R25() { return (uint64_t&)MCREG_S2(ctx.uc_mcontext); } + uint64_t& UNIX_CONTEXT::R26() { return (uint64_t&)MCREG_S3(ctx.uc_mcontext); } + uint64_t& UNIX_CONTEXT::R27() { return (uint64_t&)MCREG_S4(ctx.uc_mcontext); } + uint64_t& UNIX_CONTEXT::R28() { return (uint64_t&)MCREG_S5(ctx.uc_mcontext); } + uint64_t& UNIX_CONTEXT::R29() { return (uint64_t&)MCREG_S6(ctx.uc_mcontext); } + uint64_t& UNIX_CONTEXT::R30() { return (uint64_t&)MCREG_S7(ctx.uc_mcontext); } + uint64_t& UNIX_CONTEXT::R31() { return (uint64_t&)MCREG_S8(ctx.uc_mcontext); } + uint64_t& UNIX_CONTEXT::Fp() { return (uint64_t&)MCREG_Fp(ctx.uc_mcontext); } // R22 + uint64_t& UNIX_CONTEXT::Ra() { return (uint64_t&)MCREG_Ra(ctx.uc_mcontext); } // R1 + uint64_t& UNIX_CONTEXT::Sp() { return (uint64_t&)MCREG_Sp(ctx.uc_mcontext); } // R3 + uint64_t& UNIX_CONTEXT::Pc() { return (uint64_t&)MCREG_Pc(ctx.uc_mcontext); } + #else PORTABILITY_ASSERT("UNIX_CONTEXT"); #endif // TARGET_ARM diff --git a/src/coreclr/nativeaot/Runtime/unix/UnixContext.h b/src/coreclr/nativeaot/Runtime/unix/UnixContext.h index caddff419d373..662b697715da0 100644 --- a/src/coreclr/nativeaot/Runtime/unix/UnixContext.h +++ b/src/coreclr/nativeaot/Runtime/unix/UnixContext.h @@ -158,6 +158,61 @@ struct UNIX_CONTEXT lambda((size_t*)&R11()); lambda((size_t*)&R12()); } + +#elif defined(TARGET_LOONGARCH64) + + uint64_t& R0(); + uint64_t& R2(); + uint64_t& R4(); + uint64_t& R5(); + uint64_t& R6(); + uint64_t& R7(); + uint64_t& R8(); + uint64_t& R9(); + uint64_t& R10(); + uint64_t& R11(); + uint64_t& R12(); + uint64_t& R13(); + uint64_t& R14(); + uint64_t& R15(); + uint64_t& R16(); + uint64_t& R17(); + uint64_t& R18(); + uint64_t& R19(); + uint64_t& R20(); + uint64_t& R21(); + uint64_t& R23(); + uint64_t& R24(); + uint64_t& R25(); + uint64_t& R26(); + uint64_t& R27(); + uint64_t& R28(); + uint64_t& R29(); + uint64_t& R30(); + uint64_t& R31(); + uint64_t& Fp(); // R22 + uint64_t& Ra(); // R1 + uint64_t& Sp(); // R3 + uint64_t& Pc(); + + uintptr_t GetIp() { return (uintptr_t)Pc(); } + uintptr_t GetSp() { return (uintptr_t)Sp(); } + + template + void ForEachPossibleObjectRef(F lambda) + { + // it is doubtful anyone would implement R0,R2,R4-R21,R23-R31 not as a contiguous array + // just in case - here are some asserts. + ASSERT(&R4() + 1 == &R5()); + ASSERT(&R4() + 10 == &R14()); + + for (uint64_t* pReg = &R0(); pReg <= &R31(); pReg++) + lambda((size_t*)pReg); + + // Ra can be used as a scratch register + lambda((size_t*)&Ra()); + } + #else PORTABILITY_ASSERT("UNIX_CONTEXT"); #endif // TARGET_ARM diff --git a/src/coreclr/nativeaot/Runtime/unix/UnixNativeCodeManager.cpp b/src/coreclr/nativeaot/Runtime/unix/UnixNativeCodeManager.cpp index 3ccf59f611ecb..b12d63bf72612 100644 --- a/src/coreclr/nativeaot/Runtime/unix/UnixNativeCodeManager.cpp +++ b/src/coreclr/nativeaot/Runtime/unix/UnixNativeCodeManager.cpp @@ -399,7 +399,7 @@ bool UnixNativeCodeManager::IsUnwindable(PTR_VOID pvAddress) ASSERT(((uintptr_t)pvAddress & 1) == 0); #endif -#if defined(TARGET_ARM64) || defined(TARGET_ARM) +#if defined(TARGET_ARM64) || defined(TARGET_ARM) || defined(TARGET_LOONGARCH64) MethodInfo methodInfo; FindMethodInfo(pvAddress, &methodInfo); pMethodInfo = &methodInfo; @@ -667,6 +667,61 @@ int UnixNativeCodeManager::IsInProlog(MethodInfo * pMethodInfo, PTR_VOID pvAddre return 0; +#elif defined(TARGET_LOONGARCH64) + +// 0010 1001 11xx xxxx xxxx xxxx xxxx xxxx +#define ST_BITS 0x29C00000 +#define ST_MASK 0xFFC00000 + +// addi.d $fp, $sp, x +// ori $fp, $sp, 0 +// 0000 0010 11xx xxxx xxxx xx00 0111 0110 +#define ADDI_FP_SP_BITS 0x02C00076 +#define ADDI_FP_SP_MASK 0xFFC003FF + +#define ST_RJ_MASK 0x3E0 +#define ST_RJ_FP 0x2C0 +#define ST_RJ_RA 0x20 +#define ST_RD_MASK 0x1F +#define ST_RD_SP 0x3 +#define ST_RD_FP 0x16 + + UnixNativeMethodInfo * pNativeMethodInfo = (UnixNativeMethodInfo *)pMethodInfo; + ASSERT(pNativeMethodInfo != NULL); + + uint32_t* start = (uint32_t*)pNativeMethodInfo->pMethodStartAddress; + bool savedFp = false; + bool savedRa = false; + bool establishedFp = false; + + for (uint32_t* pInstr = (uint32_t*)start; pInstr < pvAddress && !(savedFp && savedRa && establishedFp); pInstr++) + { + uint32_t instr = *pInstr; + + if (((instr & ST_MASK) == ST_BITS) && + ((instr & ST_RD_MASK) == ST_RD_SP || (instr & ST_RD_MASK) == ST_RD_FP)) + { + // SP/FP-relative store of pair of registers + savedFp |= (instr & ST_RJ_MASK) == ST_RJ_FP; + savedRa |= (instr & ST_RJ_MASK) == ST_RJ_RA; + } + else if ((instr & ADDI_FP_SP_MASK) == ADDI_FP_SP_BITS) + { + establishedFp = true; + } + else + { + // JIT generates other patterns into the prolog that we currently don't + // recognize (saving unpaired register, stack pointer adjustments). We + // don't need to recognize these patterns unless a compact unwinding code + // is generated for them in ILC. + // https://github.com/dotnet/runtime/issues/76371 + return -1; + } + } + + return savedFp && savedRa && establishedFp ? 0 : 1; + #else return -1; @@ -1043,6 +1098,60 @@ int UnixNativeCodeManager::TrailingEpilogueInstructionsCount(MethodInfo * pMetho return 0; } +#elif defined(TARGET_LOONGARCH64) + +// ld.d +// 0010 1000 11xx xxxx xxxx xxxx xxxx xxxx +#define LD_BITS 0xB9400000 +#define LD_MASK 0xBF400000 + +// ldx.d with register offset +// 0011 1000 0000 1100 0xxx xxxx xxxx xxxx +#define LDX_BITS 0x380C0000 +#define LDX_MASK 0xFFFF7000 + +// Branches, Exception Generating and System instruction group +// 01xx xxxx xxxx xxxx xxxx xxxx xxxx xxxx +#define BEGS_BITS 0x40000000 +#define BEGS_MASK 0xC0000000 + + UnixNativeMethodInfo * pNativeMethodInfo = (UnixNativeMethodInfo *)pMethodInfo; + ASSERT(pNativeMethodInfo != NULL); + + uint32_t* start = (uint32_t*)pNativeMethodInfo->pMethodStartAddress; + + // Since we stop on branches, the search is roughly limited by the containing basic block. + // We typically examine just 1-5 instructions and in rare cases up to 30. + // + // TODO: we can also limit the search by the longest possible epilogue length, but + // we must be sure the longest length considers all possibilities, + // which is somewhat nontrivial to derive/prove. + // It does not seem urgent, but it could be nice to have a constant upper bound. + for (uint32_t* pInstr = (uint32_t*)pvAddress - 1; pInstr > start; pInstr--) + { + uint32_t instr = *pInstr; + + // check for Branches, Exception Generating and System instruction group. + // If we see such instruction before seeing FP or RA restored, we are not in an epilog. + // Note: this includes RET, BRK, branches, calls, tailcalls, fences, etc... + if ((instr & BEGS_MASK) == BEGS_BITS) + { + // not in an epilogue + break; + } + + // check for restoring FP or RA with ld.d or ldx.d + int operand = (instr >> 5) & 0x1f; + if (operand == 22 || operand == 1) + { + if ((instr & LD_MASK) == LD_BITS || + (instr & LDX_MASK) == LDX_BITS) + { + return -1; + } + } + } + #endif return 0; @@ -1085,9 +1194,9 @@ bool UnixNativeCodeManager::GetReturnAddressHijackInfo(MethodInfo * pMethodIn // Decode the GC info for the current method to determine its return type GcInfoDecoderFlags flags = DECODE_RETURN_KIND; -#if defined(TARGET_ARM) || defined(TARGET_ARM64) +#if defined(TARGET_ARM) || defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64) flags = (GcInfoDecoderFlags)(flags | DECODE_HAS_TAILCALLS); -#endif // TARGET_ARM || TARGET_ARM64 +#endif // TARGET_ARM || TARGET_ARM64 || TARGET_LOONGARCH64 GcInfoDecoder decoder(GCInfoToken(p), flags); *pRetValueKind = GetGcRefKind(decoder.GetReturnKind()); @@ -1172,6 +1281,41 @@ bool UnixNativeCodeManager::GetReturnAddressHijackInfo(MethodInfo * pMethodIn *ppvRetAddrLocation = (PTR_PTR_VOID)pRegisterSet->pLR; return true; + +#elif defined(TARGET_LOONGARCH64) + + if (decoder.HasTailCalls()) + { + // Do not hijack functions that have tail calls, since there are two problems: + // 1. When a function that tail calls another one is hijacked, the RA may be + // stored at a different location in the stack frame of the tail call target. + // So just by performing tail call, the hijacked location becomes invalid and + // unhijacking would corrupt stack by writing to that location. + // 2. There is a small window after the caller pops RA from the stack in its + // epilog and before the tail called function pushes RA in its prolog when + // the hijacked return address would not be not on the stack and so we would + // not be able to unhijack. + return false; + } + + PTR_uintptr_t pRA = pRegisterSet->pRA; + if (!VirtualUnwind(pMethodInfo, pRegisterSet)) + { + return false; + } + + if (pRegisterSet->pRA == pRA) + { + // This is the case when we are either: + // + // 1) In a leaf method that does not push RA on stack, OR + // 2) In the prolog/epilog of a non-leaf method that has not yet pushed RA on stack + // or has RA already popped off. + return false; + } + + *ppvRetAddrLocation = (PTR_PTR_VOID)pRegisterSet->pRA; + return true; #else return false; #endif // defined(TARGET_AMD64) diff --git a/src/coreclr/nativeaot/Runtime/unix/UnwindHelpers.cpp b/src/coreclr/nativeaot/Runtime/unix/UnwindHelpers.cpp index 39bf9f024be60..67701b45dd948 100644 --- a/src/coreclr/nativeaot/Runtime/unix/UnwindHelpers.cpp +++ b/src/coreclr/nativeaot/Runtime/unix/UnwindHelpers.cpp @@ -34,6 +34,8 @@ using libunwind::Registers_arm; #elif defined(TARGET_ARM64) using libunwind::Registers_arm64; using libunwind::CompactUnwinder_arm64; +#elif defined(TARGET_LOONGARCH64) +using libunwind::Registers_loongarch; #elif defined(TARGET_X86) using libunwind::Registers_x86; #else @@ -806,6 +808,286 @@ void Registers_REGDISPLAY::setVectorRegister(int num, libunwind::v128 value) #endif // TARGET_ARM64 +#if defined(TARGET_LOONGARCH64) + +// Shim that implements methods required by libunwind over REGDISPLAY +struct Registers_REGDISPLAY : REGDISPLAY +{ + inline static int getArch() { return libunwind::REGISTERS_LOONGARCH; } + inline static int lastDwarfRegNum() { return _LIBUNWIND_HIGHEST_DWARF_REGISTER_LOONGARCH; } + + bool validRegister(int num) const; + bool validFloatRegister(int num) { return false; }; + bool validVectorRegister(int num) const; + + uint64_t getRegister(int num) const; + void setRegister(int num, uint64_t value, uint64_t location); + + double getFloatRegister(int num) const {abort();} + void setFloatRegister(int num, double value) {abort();} + + libunwind::v128 getVectorRegister(int num) const; + void setVectorRegister(int num, libunwind::v128 value); + + uint64_t getSP() const { return SP;} + void setSP(uint64_t value, uint64_t location) { SP = value;} + uint64_t getIP() const { return IP;} + void setIP(uint64_t value, uint64_t location) { IP = value; } + uint64_t getFP() const { return *pFP;} + void setFP(uint64_t value, uint64_t location) { pFP = (PTR_uintptr_t)location;} +}; + +inline bool Registers_REGDISPLAY::validRegister(int num) const { + if (num == UNW_REG_SP || num == UNW_LOONGARCH_R3) + return true; + + if (num == UNW_LOONGARCH_R22) + return true; + + if (num == UNW_REG_IP) + return true; + + if (num >= UNW_LOONGARCH_R0 && num <= UNW_LOONGARCH_R31) + return true; + + return false; +} + +bool Registers_REGDISPLAY::validVectorRegister(int num) const +{ + if (num >= UNW_LOONGARCH_F24 && num <= UNW_LOONGARCH_F31) + return true; + + return false; +} + +inline uint64_t Registers_REGDISPLAY::getRegister(int regNum) const { + if (regNum == UNW_REG_SP || regNum == UNW_LOONGARCH_R3) + return SP; + + if (regNum == UNW_LOONGARCH_R22) + return *pFP; + + if (regNum == UNW_LOONGARCH_R1) + return *pRA; + + if (regNum == UNW_REG_IP) + return IP; + + switch (regNum) + { + case (UNW_LOONGARCH_R0): + return *pR0; + case (UNW_LOONGARCH_R2): + return *pR2; + case (UNW_LOONGARCH_R4): + return *pR4; + case (UNW_LOONGARCH_R5): + return *pR5; + case (UNW_LOONGARCH_R6): + return *pR6; + case (UNW_LOONGARCH_R7): + return *pR7; + case (UNW_LOONGARCH_R8): + return *pR8; + case (UNW_LOONGARCH_R9): + return *pR9; + case (UNW_LOONGARCH_R10): + return *pR10; + case (UNW_LOONGARCH_R11): + return *pR11; + case (UNW_LOONGARCH_R12): + return *pR12; + case (UNW_LOONGARCH_R13): + return *pR13; + case (UNW_LOONGARCH_R14): + return *pR14; + case (UNW_LOONGARCH_R15): + return *pR15; + case (UNW_LOONGARCH_R16): + return *pR16; + case (UNW_LOONGARCH_R17): + return *pR17; + case (UNW_LOONGARCH_R18): + return *pR18; + case (UNW_LOONGARCH_R19): + return *pR19; + case (UNW_LOONGARCH_R20): + return *pR20; + case (UNW_LOONGARCH_R21): + return *pR21; + case (UNW_LOONGARCH_R23): + return *pR23; + case (UNW_LOONGARCH_R24): + return *pR24; + case (UNW_LOONGARCH_R25): + return *pR25; + case (UNW_LOONGARCH_R26): + return *pR26; + case (UNW_LOONGARCH_R27): + return *pR27; + case (UNW_LOONGARCH_R28): + return *pR28; + case (UNW_LOONGARCH_R29): + return *pR29; + case (UNW_LOONGARCH_R30): + return *pR30; + case (UNW_LOONGARCH_R31): + return *pR31; + } + + PORTABILITY_ASSERT("unsupported loongarch64 register"); +} + +void Registers_REGDISPLAY::setRegister(int num, uint64_t value, uint64_t location) +{ + if (num == UNW_REG_SP || num == UNW_LOONGARCH_R3) { + SP = (uintptr_t )value; + return; + } + + if (num == UNW_LOONGARCH_R22) { + pFP = (PTR_uintptr_t)location; + return; + } + + if (num == UNW_LOONGARCH_R1) { + pRA = (PTR_uintptr_t)location; + return; + } + + if (num == UNW_REG_IP) { + IP = value; + return; + } + + switch (num) + { + case (UNW_LOONGARCH_R0): + pR0 = (PTR_uintptr_t)location; + break; + case (UNW_LOONGARCH_R2): + pR2 = (PTR_uintptr_t)location; + break; + case (UNW_LOONGARCH_R4): + pR4 = (PTR_uintptr_t)location; + break; + case (UNW_LOONGARCH_R5): + pR5 = (PTR_uintptr_t)location; + break; + case (UNW_LOONGARCH_R6): + pR6 = (PTR_uintptr_t)location; + break; + case (UNW_LOONGARCH_R7): + pR7 = (PTR_uintptr_t)location; + break; + case (UNW_LOONGARCH_R8): + pR8 = (PTR_uintptr_t)location; + break; + case (UNW_LOONGARCH_R9): + pR9 = (PTR_uintptr_t)location; + break; + case (UNW_LOONGARCH_R10): + pR10 = (PTR_uintptr_t)location; + break; + case (UNW_LOONGARCH_R11): + pR11 = (PTR_uintptr_t)location; + break; + case (UNW_LOONGARCH_R12): + pR12 = (PTR_uintptr_t)location; + break; + case (UNW_LOONGARCH_R13): + pR13 = (PTR_uintptr_t)location; + break; + case (UNW_LOONGARCH_R14): + pR14 = (PTR_uintptr_t)location; + break; + case (UNW_LOONGARCH_R15): + pR15 = (PTR_uintptr_t)location; + break; + case (UNW_LOONGARCH_R16): + pR16 = (PTR_uintptr_t)location; + break; + case (UNW_LOONGARCH_R17): + pR17 = (PTR_uintptr_t)location; + break; + case (UNW_LOONGARCH_R18): + pR18 = (PTR_uintptr_t)location; + break; + case (UNW_LOONGARCH_R19): + pR19 = (PTR_uintptr_t)location; + break; + case (UNW_LOONGARCH_R20): + pR20 = (PTR_uintptr_t)location; + break; + case (UNW_LOONGARCH_R21): + pR21 = (PTR_uintptr_t)location; + break; + case (UNW_LOONGARCH_R23): + pR23 = (PTR_uintptr_t)location; + break; + case (UNW_LOONGARCH_R24): + pR24 = (PTR_uintptr_t)location; + break; + case (UNW_LOONGARCH_R25): + pR25 = (PTR_uintptr_t)location; + break; + case (UNW_LOONGARCH_R26): + pR26 = (PTR_uintptr_t)location; + break; + case (UNW_LOONGARCH_R27): + pR27 = (PTR_uintptr_t)location; + break; + case (UNW_LOONGARCH_R28): + pR28 = (PTR_uintptr_t)location; + break; + case (UNW_LOONGARCH_R29): + pR29 = (PTR_uintptr_t)location; + break; + case (UNW_LOONGARCH_R30): + pR30 = (PTR_uintptr_t)location; + break; + case (UNW_LOONGARCH_R31): + pR31 = (PTR_uintptr_t)location; + break; + default: + PORTABILITY_ASSERT("unsupported loongarch64 register"); + } +} + +libunwind::v128 Registers_REGDISPLAY::getVectorRegister(int num) const +{ + num -= UNW_LOONGARCH_F24; + + if (num < 0 || num >= sizeof(F) / sizeof(uint64_t)) + { + PORTABILITY_ASSERT("unsupported loongarch64 vector register"); + } + + libunwind::v128 result; + + result.vec[0] = 0; + result.vec[1] = 0; + result.vec[2] = F[num] >> 32; + result.vec[3] = F[num] & 0xFFFFFFFF; + + return result; +} + +void Registers_REGDISPLAY::setVectorRegister(int num, libunwind::v128 value) +{ + num -= UNW_LOONGARCH_F24; + + if (num < 0 || num >= sizeof(F) / sizeof(uint64_t)) + { + PORTABILITY_ASSERT("unsupported loongarch64 vector register"); + } + + F[num] = (uint64_t)value.vec[2] << 32 | (uint64_t)value.vec[3]; +} + +#endif // TARGET_LOONGARCH64 + bool UnwindHelpers::StepFrame(REGDISPLAY *regs, unw_word_t start_ip, uint32_t format, unw_word_t unwind_info) { #if _LIBUNWIND_SUPPORT_DWARF_UNWIND @@ -818,6 +1100,12 @@ bool UnwindHelpers::StepFrame(REGDISPLAY *regs, unw_word_t start_ip, uint32_t fo int stepRet = compactInst.stepWithCompactEncoding(format, start_ip, _addressSpace, *(Registers_REGDISPLAY*)regs); return stepRet == UNW_STEP_SUCCESS; } +#elif defined(TARGET_LOONGARCH64) + if ((format & UNWIND_LOONGARCH64_MODE_MASK) != UNWIND_LOONGARCH64_MODE_DWARF) { + CompactUnwinder_loongarch64 compactInst; + int stepRet = compactInst.stepWithCompactEncoding(format, start_ip, _addressSpace, *(Registers_REGDISPLAY*)regs); + return stepRet == UNW_STEP_SUCCESS; + } #elif defined(TARGET_AMD64) if ((format & UNWIND_X86_64_MODE_MASK) != UNWIND_X86_64_MODE_DWARF) { CompactUnwinder_x86_64 compactInst; @@ -867,6 +1155,8 @@ bool UnwindHelpers::GetUnwindProcInfo(PCODE pc, UnwindInfoSections &uwInfoSectio libunwind::UnwindCursor uc(_addressSpace); #elif defined(HOST_X86) libunwind::UnwindCursor uc(_addressSpace); +#elif defined(HOST_LOONGARCH64) + libunwind::UnwindCursor uc(_addressSpace); #else #error "Unwinding is not implemented for this architecture yet." #endif @@ -885,6 +1175,12 @@ bool UnwindHelpers::GetUnwindProcInfo(PCODE pc, UnwindInfoSections &uwInfoSectio } else { dwarfOffsetHint = procInfo->format & UNWIND_ARM64_DWARF_SECTION_OFFSET; } +#elif defined(TARGET_LOONGARCH64) + if ((procInfo->format & UNWIND_LOONGARCH64_MODE_MASK) != UNWIND_LOONGARCH64_MODE_DWARF) { + return true; + } else { + dwarfOffsetHint = procInfo->format & UNWIND_LOONGARCH64_DWARF_SECTION_OFFSET; + } #elif defined(TARGET_AMD64) if ((procInfo->format & UNWIND_X86_64_MODE_MASK) != UNWIND_X86_64_MODE_DWARF) { return true; diff --git a/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeSystemContextFactory.cs b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeSystemContextFactory.cs index 06db5253c590c..c7a96e12ddf17 100644 --- a/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeSystemContextFactory.cs +++ b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeSystemContextFactory.cs @@ -42,6 +42,8 @@ public static TypeSystemContext Create() TargetArchitecture.X64, #elif TARGET_WASM TargetArchitecture.Wasm32, +#elif TARGET_LOONGARCH64 + TargetArchitecture.LoongArch64, #else #error Unknown architecture #endif diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_LoongArch64/LoongArch64ReadyToRunHelperNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_LoongArch64/LoongArch64ReadyToRunHelperNode.cs index db4d1855e20fb..8656626e85406 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_LoongArch64/LoongArch64ReadyToRunHelperNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_LoongArch64/LoongArch64ReadyToRunHelperNode.cs @@ -48,6 +48,7 @@ protected override void EmitCode(NodeFactory factory, ref LoongArch64Emitter enc case ReadyToRunHelperId.GetThreadStaticBase: { MetadataType target = (MetadataType)Target; + encoder.EmitMOV(encoder.TargetRegister.Arg2, factory.TypeThreadStaticIndex(target)); // First arg: address of the TypeManager slot that provides the helper with diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/JitHelper.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/JitHelper.cs index c04658df379f7..54f80651288da 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/JitHelper.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/JitHelper.cs @@ -62,14 +62,19 @@ public static void GetEntryPoint(TypeSystemContext context, ReadyToRunHelper id, break; case ReadyToRunHelper.WriteBarrier: - mangledName = context.Target.Architecture == TargetArchitecture.ARM64 ? "RhpAssignRefArm64" : "RhpAssignRef"; + mangledName = context.Target.Architecture switch + { + TargetArchitecture.ARM64 => "RhpAssignRefArm64", + TargetArchitecture.LoongArch64 => "RhpAssignRefLoongArch64", + _ => "RhpAssignRef" + }; break; case ReadyToRunHelper.CheckedWriteBarrier: mangledName = context.Target.Architecture == TargetArchitecture.ARM64 ? "RhpCheckedAssignRefArm64" : "RhpCheckedAssignRef"; break; case ReadyToRunHelper.BulkWriteBarrier: - mangledName = "RhBuffer_BulkMoveWithWriteBarrier"; - break; + mangledName = "RhBuffer_BulkMoveWithWriteBarrier"; + break; case ReadyToRunHelper.ByRefWriteBarrier: mangledName = context.Target.Architecture == TargetArchitecture.ARM64 ? "RhpByRefAssignRefArm64" : "RhpByRefAssignRef"; break; diff --git a/src/native/external/llvm-libunwind/include/__libunwind_config.h b/src/native/external/llvm-libunwind/include/__libunwind_config.h index ecfe7be0d12f6..d521890f17f86 100644 --- a/src/native/external/llvm-libunwind/include/__libunwind_config.h +++ b/src/native/external/llvm-libunwind/include/__libunwind_config.h @@ -173,8 +173,8 @@ #elif defined(__loongarch__) #define _LIBUNWIND_TARGET_LOONGARCH 1 #if __loongarch_grlen == 64 -#define _LIBUNWIND_CONTEXT_SIZE 65 -#define _LIBUNWIND_CURSOR_SIZE 77 +#define _LIBUNWIND_CONTEXT_SIZE 98 +#define _LIBUNWIND_CURSOR_SIZE 110 #elif defined(HOST_WASM) #define _LIBUNWIND_TARGET_WASM 1 // TODO: Determine the right values diff --git a/src/native/external/llvm-libunwind/src/Registers.hpp b/src/native/external/llvm-libunwind/src/Registers.hpp index 4e1d75519ef35..b76f24ea67da5 100644 --- a/src/native/external/llvm-libunwind/src/Registers.hpp +++ b/src/native/external/llvm-libunwind/src/Registers.hpp @@ -5268,13 +5268,14 @@ class _LIBUNWIND_HIDDEN Registers_loongarch { bool validRegister(int num) const; uint64_t getRegister(int num) const; - void setRegister(int num, uint64_t value); + void setRegister(int num, uint64_t value, uint64_t location); bool validFloatRegister(int num) const; double getFloatRegister(int num) const; void setFloatRegister(int num, double value); bool validVectorRegister(int num) const; v128 getVectorRegister(int num) const; void setVectorRegister(int num, v128 value); + uint64_t getRegisterLocation(int num) const; static const char *getRegisterName(int num); void jumpto(); static constexpr int lastDwarfRegNum() { @@ -5283,17 +5284,23 @@ class _LIBUNWIND_HIDDEN Registers_loongarch { static int getArch() { return REGISTERS_LOONGARCH; } uint64_t getSP() const { return _registers.__r[3]; } - void setSP(uint64_t value) { _registers.__r[3] = value; } + void setSP(uint64_t value, uint64_t location) { _registers.__r[3] = value; } uint64_t getIP() const { return _registers.__pc; } - void setIP(uint64_t value) { _registers.__pc = value; } + void setIP(uint64_t value, uint64_t location) { _registers.__pc = value; } private: - struct loongarch_thread_state_t { + struct GPRs { + uint64_t __r[32]; + uint64_t __pc; + }; + + struct GPRLocations { uint64_t __r[32]; uint64_t __pc; }; - loongarch_thread_state_t _registers; + GPRs _registers; + GPRLocations _registerLocations; #if __loongarch_frlen == 64 double _floats[32]; #endif @@ -5303,6 +5310,7 @@ inline Registers_loongarch::Registers_loongarch(const void *registers) { static_assert((check_fit::does_fit), "loongarch registers do not fit into unw_context_t"); memcpy(&_registers, registers, sizeof(_registers)); + memset(&_registerLocations, 0, sizeof(_registerLocations)); static_assert(sizeof(_registers) == 0x108, "expected float registers to be at offset 264"); #if __loongarch_frlen == 64 @@ -5313,6 +5321,7 @@ inline Registers_loongarch::Registers_loongarch(const void *registers) { inline Registers_loongarch::Registers_loongarch() { memset(&_registers, 0, sizeof(_registers)); + memset(&_registerLocations, 0, sizeof(_registerLocations)); #if __loongarch_frlen == 64 memset(&_floats, 0, sizeof(_floats)); #endif @@ -5337,17 +5346,33 @@ inline uint64_t Registers_loongarch::getRegister(int regNum) const { _LIBUNWIND_ABORT("unsupported loongarch register"); } -inline void Registers_loongarch::setRegister(int regNum, uint64_t value) { - if (regNum >= UNW_LOONGARCH_R0 && regNum <= UNW_LOONGARCH_R31) +inline void Registers_loongarch::setRegister(int regNum, uint64_t value, uint64_t location) { + if (regNum >= UNW_LOONGARCH_R0 && regNum <= UNW_LOONGARCH_R31) { _registers.__r[regNum - UNW_LOONGARCH_R0] = value; - else if (regNum == UNW_REG_IP) + _registerLocations.__r[regNum - UNW_LOONGARCH_R0] = location; + } + else if (regNum == UNW_REG_IP) { _registers.__pc = value; - else if (regNum == UNW_REG_SP) + _registerLocations.__pc = location; + } + else if (regNum == UNW_REG_SP) { _registers.__r[3] = value; + _registerLocations.__r[3] = location; + } else _LIBUNWIND_ABORT("unsupported loongarch register"); } +inline uint64_t Registers_loongarch::getRegisterLocation(int regNum) const { + if (regNum == UNW_REG_IP) + return _registerLocations.__pc; + if (regNum == UNW_REG_SP) + return _registerLocations.__r[3]; + if ((regNum >= 0) && (regNum < 32)) + return _registerLocations.__r[regNum]; + _LIBUNWIND_ABORT("unsupported loongarch64 register"); +} + inline const char *Registers_loongarch::getRegisterName(int regNum) { switch (regNum) { case UNW_REG_IP: