From 6fe3ac0795b605ce979d6939bc474c12a46f120a Mon Sep 17 00:00:00 2001 From: Dong-Heon Jung Date: Wed, 26 Oct 2022 17:15:18 +0900 Subject: [PATCH 01/16] [RISC-V] coreclr-vm and other directories in coreclr - Successfully cross-build for RISC-V. - Run A simple application "helloworld" - Fail a test in clr.paltest --- src/coreclr/clrdefinitions.cmake | 12 +- src/coreclr/dlls/mscordac/CMakeLists.txt | 2 + src/coreclr/gc/env/gcenv.base.h | 5 + src/coreclr/gc/env/volatile.h | 6 +- src/coreclr/gcdump/gcdumpnonx86.cpp | 3 + src/coreclr/gcinfo/CMakeLists.txt | 8 +- src/coreclr/gcinfo/gcinfodumper.cpp | 47 +- src/coreclr/gcinfo/gcinfoencoder.cpp | 16 +- src/coreclr/inc/clrconfigvalues.h | 9 +- src/coreclr/inc/clrnt.h | 58 + src/coreclr/inc/cordebuginfo.h | 36 + src/coreclr/inc/crosscomp.h | 142 ++ src/coreclr/inc/eetwain.h | 8 +- src/coreclr/inc/eexcp.h | 4 +- src/coreclr/inc/gcinfodecoder.h | 10 +- src/coreclr/inc/gcinfoencoder.h | 4 +- src/coreclr/inc/gcinfotypes.h | 59 +- src/coreclr/inc/jithelpers.h | 2 +- src/coreclr/inc/pedecoder.h | 2 + src/coreclr/inc/regdisp.h | 68 +- src/coreclr/inc/switches.h | 2 +- src/coreclr/inc/targetosarch.h | 13 + src/coreclr/inc/volatile.h | 4 +- src/coreclr/scripts/coreclr_arguments.py | 2 +- .../superpmi/superpmi-shared/spmiutil.cpp | 2 + .../tools/superpmi/superpmi-shared/spmiutil.h | 5 +- src/coreclr/unwinder/riscv64/unwinder.cpp | 1464 ++++++++++++++++- src/coreclr/unwinder/riscv64/unwinder.h | 40 +- src/coreclr/utilcode/util.cpp | 14 +- src/coreclr/vm/CMakeLists.txt | 25 +- src/coreclr/vm/arm64/cgencpu.h | 1 - src/coreclr/vm/callcounting.h | 2 + src/coreclr/vm/callingconvention.h | 241 ++- src/coreclr/vm/ceeload.h | 2 + src/coreclr/vm/codeman.cpp | 75 +- src/coreclr/vm/codeman.h | 4 +- src/coreclr/vm/dynamicmethod.cpp | 2 +- src/coreclr/vm/eetwain.cpp | 4 +- src/coreclr/vm/encee.cpp | 2 +- src/coreclr/vm/excep.cpp | 12 +- src/coreclr/vm/fieldmarshaler.cpp | 2 + src/coreclr/vm/fieldmarshaler.h | 15 +- src/coreclr/vm/frames.h | 5 +- src/coreclr/vm/gccover.cpp | 40 +- src/coreclr/vm/gccover.h | 9 + src/coreclr/vm/gcenv.ee.cpp | 6 +- src/coreclr/vm/gcinfodecoder.cpp | 154 +- src/coreclr/vm/interpreter.cpp | 36 +- src/coreclr/vm/interpreter.h | 4 + src/coreclr/vm/jitinterface.cpp | 16 + src/coreclr/vm/jitinterface.h | 2 +- src/coreclr/vm/methodtable.cpp | 610 +++++++ src/coreclr/vm/methodtable.h | 5 + src/coreclr/vm/precode.cpp | 4 + src/coreclr/vm/precode.h | 19 +- src/coreclr/vm/prestub.cpp | 2 +- src/coreclr/vm/riscv64/asmconstants.h | 251 ++- src/coreclr/vm/riscv64/asmhelpers.S | 952 ++++++++++- .../vm/riscv64/calldescrworkerloongarch64.S | 7 - .../vm/riscv64/calldescrworkerriscv64.S | 205 +++ src/coreclr/vm/riscv64/cgencpu.h | 503 +++++- src/coreclr/vm/riscv64/crthelpers.S | 32 +- src/coreclr/vm/riscv64/excepcpu.h | 45 +- src/coreclr/vm/riscv64/gmscpu.h | 100 +- src/coreclr/vm/riscv64/pinvokestubs.S | 183 ++- src/coreclr/vm/riscv64/profiler.cpp | 307 +++- src/coreclr/vm/riscv64/stubs.cpp | 1251 +++++++++++++- src/coreclr/vm/riscv64/thunktemplates.S | 32 +- src/coreclr/vm/riscv64/virtualcallstubcpu.hpp | 565 ++++++- src/coreclr/vm/stackwalk.cpp | 6 +- src/coreclr/vm/stackwalk.h | 4 +- src/coreclr/vm/stublink.cpp | 1 - src/coreclr/vm/stubmgr.cpp | 2 +- src/coreclr/vm/threads.cpp | 16 +- src/coreclr/vm/threadsuspend.cpp | 8 +- 75 files changed, 7673 insertions(+), 143 deletions(-) delete mode 100644 src/coreclr/vm/riscv64/calldescrworkerloongarch64.S create mode 100644 src/coreclr/vm/riscv64/calldescrworkerriscv64.S diff --git a/src/coreclr/clrdefinitions.cmake b/src/coreclr/clrdefinitions.cmake index 9472c5f88ad720..b5a43f5057a47b 100644 --- a/src/coreclr/clrdefinitions.cmake +++ b/src/coreclr/clrdefinitions.cmake @@ -197,12 +197,12 @@ if (CLR_CMAKE_TARGET_ARCH_AMD64) add_definitions(-DUNIX_AMD64_ABI_ITF) endif (CLR_CMAKE_TARGET_ARCH_AMD64) add_definitions(-DFEATURE_USE_ASM_GC_WRITE_BARRIERS) -if(CLR_CMAKE_TARGET_ARCH_AMD64 OR CLR_CMAKE_TARGET_ARCH_ARM64 OR CLR_CMAKE_TARGET_ARCH_LOONGARCH64) +if(CLR_CMAKE_TARGET_ARCH_AMD64 OR CLR_CMAKE_TARGET_ARCH_ARM64 OR CLR_CMAKE_TARGET_ARCH_LOONGARCH64 OR CLR_CMAKE_TARGET_ARCH_RISCV64) add_definitions(-DFEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP) -endif(CLR_CMAKE_TARGET_ARCH_AMD64 OR CLR_CMAKE_TARGET_ARCH_ARM64 OR CLR_CMAKE_TARGET_ARCH_LOONGARCH64) -if(CLR_CMAKE_TARGET_ARCH_AMD64 OR CLR_CMAKE_TARGET_ARCH_ARM64 OR CLR_CMAKE_TARGET_ARCH_LOONGARCH64) +endif(CLR_CMAKE_TARGET_ARCH_AMD64 OR CLR_CMAKE_TARGET_ARCH_ARM64 OR CLR_CMAKE_TARGET_ARCH_LOONGARCH64 OR CLR_CMAKE_TARGET_ARCH_RISCV64) +if(CLR_CMAKE_TARGET_ARCH_AMD64 OR CLR_CMAKE_TARGET_ARCH_ARM64 OR CLR_CMAKE_TARGET_ARCH_LOONGARCH64 OR CLR_CMAKE_TARGET_ARCH_RISCV64) add_definitions(-DFEATURE_MANUALLY_MANAGED_CARD_BUNDLES) -endif(CLR_CMAKE_TARGET_ARCH_AMD64 OR CLR_CMAKE_TARGET_ARCH_ARM64 OR CLR_CMAKE_TARGET_ARCH_LOONGARCH64) +endif(CLR_CMAKE_TARGET_ARCH_AMD64 OR CLR_CMAKE_TARGET_ARCH_ARM64 OR CLR_CMAKE_TARGET_ARCH_LOONGARCH64 OR CLR_CMAKE_TARGET_ARCH_RISCV64) if(NOT CLR_CMAKE_TARGET_UNIX) add_definitions(-DFEATURE_WIN32_REGISTRY) @@ -275,6 +275,10 @@ function(set_target_definitions_to_custom_os_and_arch) target_compile_definitions(${TARGETDETAILS_TARGET} PRIVATE FEATURE_MULTIREG_RETURN) elseif((TARGETDETAILS_ARCH STREQUAL "arm") OR (TARGETDETAILS_ARCH STREQUAL "armel")) target_compile_definitions(${TARGETDETAILS_TARGET} PRIVATE TARGET_ARM) + elseif((TARGETDETAILS_ARCH STREQUAL "riscv64")) + target_compile_definitions(${TARGETDETAILS_TARGET} PRIVATE TARGET_64BIT) + target_compile_definitions(${TARGETDETAILS_TARGET} PRIVATE TARGET_RISCV64) + target_compile_definitions(${TARGETDETAILS_TARGET} PRIVATE FEATURE_MULTIREG_RETURN) endif() if (TARGETDETAILS_ARCH STREQUAL "armel") diff --git a/src/coreclr/dlls/mscordac/CMakeLists.txt b/src/coreclr/dlls/mscordac/CMakeLists.txt index 25c2532358774b..ee29ceb80b2d5b 100644 --- a/src/coreclr/dlls/mscordac/CMakeLists.txt +++ b/src/coreclr/dlls/mscordac/CMakeLists.txt @@ -49,6 +49,8 @@ else(CLR_CMAKE_HOST_WIN32) if (CLR_CMAKE_HOST_ARCH_ARM OR CLR_CMAKE_HOST_ARCH_ARM64 OR CLR_CMAKE_HOST_ARCH_LOONGARCH64) set(JUMP_INSTRUCTION b) + elseif (CLR_CMAKE_HOST_ARCH_RISCV64) + set(JUMP_INSTRUCTION tail) else() set(JUMP_INSTRUCTION jmp) endif() diff --git a/src/coreclr/gc/env/gcenv.base.h b/src/coreclr/gc/env/gcenv.base.h index c6a73eb6afea5b..d6f6e6161b2543 100644 --- a/src/coreclr/gc/env/gcenv.base.h +++ b/src/coreclr/gc/env/gcenv.base.h @@ -226,6 +226,11 @@ typedef DWORD (WINAPI *PTHREAD_START_ROUTINE)(void* lpThreadParameter); #define MemoryBarrier __sync_synchronize #endif // __loongarch64 +#ifdef __riscv // TODO RISCV64 + #define YieldProcessor() asm volatile( "fence iorw, iorw"); // TODO + #define MemoryBarrier __sync_synchronize +#endif // __riscv + #endif // _MSC_VER #ifdef _MSC_VER diff --git a/src/coreclr/gc/env/volatile.h b/src/coreclr/gc/env/volatile.h index bef01f680e5465..c40e5c2d60964e 100644 --- a/src/coreclr/gc/env/volatile.h +++ b/src/coreclr/gc/env/volatile.h @@ -66,8 +66,8 @@ #error The Volatile type is currently only defined for Visual C++ and GNU C++ #endif -#if defined(__GNUC__) && !defined(HOST_X86) && !defined(HOST_AMD64) && !defined(HOST_ARM) && !defined(HOST_ARM64) && !defined(HOST_LOONGARCH64) && !defined(HOST_WASM) -#error The Volatile type is currently only defined for GCC when targeting x86, AMD64, ARM, ARM64, LOONGARCH64 or Wasm +#if defined(__GNUC__) && !defined(HOST_X86) && !defined(HOST_AMD64) && !defined(HOST_ARM) && !defined(HOST_ARM64) && !defined(HOST_LOONGARCH64) && !defined(HOST_WASM) && !defined(HOST_RISCV64) +#error The Volatile type is currently only defined for GCC when targeting x86, AMD64, ARM, ARM64, LOONGARCH64, Wasm, RISCV64 #endif #if defined(__GNUC__) @@ -76,6 +76,8 @@ #define VOLATILE_MEMORY_BARRIER() asm volatile ("dmb ish" : : : "memory") #elif defined(HOST_LOONGARCH64) #define VOLATILE_MEMORY_BARRIER() asm volatile ("dbar 0 " : : : "memory") +#elif defined(HOST_RISCV64) +#define VOLATILE_MEMORY_BARRIER() asm volatile ("fence rw,rw" : : : "memory") #else // // For GCC, we prevent reordering by the compiler by inserting the following after a volatile diff --git a/src/coreclr/gcdump/gcdumpnonx86.cpp b/src/coreclr/gcdump/gcdumpnonx86.cpp index f93e9bd42ca3ac..336dede0d608b3 100644 --- a/src/coreclr/gcdump/gcdumpnonx86.cpp +++ b/src/coreclr/gcdump/gcdumpnonx86.cpp @@ -72,6 +72,9 @@ PCSTR GetRegName (UINT32 regnum) #elif defined(TARGET_LOONGARCH64) assert(!"unimplemented on LOONGARCH yet"); return "???"; +#elif defined(TARGET_RISCV64) + assert(!"unimplemented on RISCV64 yet"); + return "???"; #endif } diff --git a/src/coreclr/gcinfo/CMakeLists.txt b/src/coreclr/gcinfo/CMakeLists.txt index a9d8a6f5848d0f..f1dcd7fc89d3a8 100644 --- a/src/coreclr/gcinfo/CMakeLists.txt +++ b/src/coreclr/gcinfo/CMakeLists.txt @@ -79,8 +79,12 @@ if (CLR_CMAKE_TARGET_ARCH_LOONGARCH64) create_gcinfo_lib(TARGET gcinfo_unix_loongarch64 OS unix ARCH loongarch64) endif (CLR_CMAKE_TARGET_ARCH_LOONGARCH64) -create_gcinfo_lib(TARGET gcinfo_universal_arm OS universal ARCH arm) -create_gcinfo_lib(TARGET gcinfo_win_x86 OS win ARCH x86) +if (CLR_CMAKE_TARGET_ARCH_RISCV64) + create_gcinfo_lib(TARGET gcinfo_unix_riscv64 OS unix ARCH riscv64) +else() + create_gcinfo_lib(TARGET gcinfo_universal_arm OS universal ARCH arm) + create_gcinfo_lib(TARGET gcinfo_win_x86 OS win ARCH x86) +endif (CLR_CMAKE_TARGET_ARCH_RISCV64) if (CLR_CMAKE_TARGET_ARCH_I386 AND CLR_CMAKE_TARGET_UNIX) create_gcinfo_lib(TARGET gcinfo_unix_x86 OS unix ARCH x86) diff --git a/src/coreclr/gcinfo/gcinfodumper.cpp b/src/coreclr/gcinfo/gcinfodumper.cpp index ba70d9c68c8578..be45bc3feb2ee2 100644 --- a/src/coreclr/gcinfo/gcinfodumper.cpp +++ b/src/coreclr/gcinfo/gcinfodumper.cpp @@ -224,6 +224,45 @@ BOOL GcInfoDumper::ReportPointerRecord ( REG(ra, Ra), { offsetof(T_CONTEXT, Sp) }, #undef REG +#elif defined(TARGET_RISCV64) +#undef REG +#define REG(reg, field) { offsetof(Riscv64VolatileContextPointer, field) } + REG(zero, R0), + REG(a0, A0), + REG(a1, A1), + REG(a2, A2), + REG(a3, A3), + REG(a4, A4), + REG(a5, A5), + REG(a6, A6), + REG(a7, A7), + REG(t0, T0), + REG(t1, T1), + REG(t2, T2), + REG(t3, T3), + REG(t4, T4), + REG(t5, T5), + REG(t6, T6), +#undef REG +#define REG(reg, field) { offsetof(T_KNONVOLATILE_CONTEXT_POINTERS, field) } + REG(s1, S1), + REG(s2, S2), + REG(s3, S3), + REG(s4, S4), + REG(s5, S5), + REG(s6, S6), + REG(s7, S7), + REG(s8, S8), + REG(s9, S9), + REG(s10, S10), + REG(s11, S11), + REG(ra, Ra), + REG(gp, Gp), + REG(tp, Tp), + REG(fp, Fp), + { offsetof(T_CONTEXT, Sp) }, +#undef REG + #else PORTABILITY_ASSERT("GcInfoDumper::ReportPointerRecord is not implemented on this platform.") #endif @@ -248,6 +287,9 @@ PORTABILITY_ASSERT("GcInfoDumper::ReportPointerRecord is not implemented on this #elif defined(TARGET_LOONGARCH64) assert(!"unimplemented on LOONGARCH yet"); iSPRegister = 0; +#elif defined(TARGET_RISCV64) + assert(!"unimplemented on RISCV64 yet"); + iSPRegister = 0; #endif #if defined(TARGET_ARM) || defined(TARGET_ARM64) @@ -660,8 +702,11 @@ GcInfoDumper::EnumerateStateChangesResults GcInfoDumper::EnumerateStateChanges ( #elif defined(TARGET_LOONGARCH64) #pragma message("Unimplemented for LOONGARCH64 yet.") assert(!"unimplemented on LOONGARCH yet"); +#elif defined(TARGET_RISCV64) +#pragma message("Unimplemented for RISCV64 yet.") + assert(!"unimplemented on RISCV64 yet"); // TODO RISCV64 #else -PORTABILITY_ASSERT("GcInfoDumper::EnumerateStateChanges is not implemented on this platform.") +PORTABILITY_ASSERT("GcInfoDumper::EnumerateStateChanges is not implemented on this platform."); #endif #undef FILL_REGS diff --git a/src/coreclr/gcinfo/gcinfoencoder.cpp b/src/coreclr/gcinfo/gcinfoencoder.cpp index 9564e3622ee6fd..73d0e29ca5f40d 100644 --- a/src/coreclr/gcinfo/gcinfoencoder.cpp +++ b/src/coreclr/gcinfo/gcinfoencoder.cpp @@ -479,7 +479,7 @@ GcInfoEncoder::GcInfoEncoder( m_ReversePInvokeFrameSlot = NO_REVERSE_PINVOKE_FRAME; #ifdef TARGET_AMD64 m_WantsReportOnlyLeaf = false; -#elif defined(TARGET_ARM) || defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64) +#elif defined(TARGET_ARM) || defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64) m_HasTailCalls = false; #endif // TARGET_AMD64 m_IsVarArg = false; @@ -729,6 +729,8 @@ void GcInfoEncoder::SetStackBaseRegister( UINT32 regNum ) _ASSERTE( m_StackBaseRegister == NO_STACK_BASE_REGISTER || m_StackBaseRegister == regNum ); #if defined(TARGET_LOONGARCH64) assert(regNum == 3 || 22 == regNum); +#elif defined(TARGET_RISCV64) + assert(regNum == 2 || 8 == regNum); #endif m_StackBaseRegister = regNum; } @@ -752,7 +754,7 @@ void GcInfoEncoder::SetWantsReportOnlyLeaf() { m_WantsReportOnlyLeaf = true; } -#elif defined(TARGET_ARM) || defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64) +#elif defined(TARGET_ARM) || defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64) void GcInfoEncoder::SetHasTailCalls() { m_HasTailCalls = true; @@ -1011,7 +1013,7 @@ void GcInfoEncoder::Build() (m_SizeOfEditAndContinuePreservedArea == NO_SIZE_OF_EDIT_AND_CONTINUE_PRESERVED_AREA) && #ifdef TARGET_AMD64 !m_WantsReportOnlyLeaf && -#elif defined(TARGET_ARM) || defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64) +#elif defined(TARGET_ARM) || defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64) !m_HasTailCalls && #endif // TARGET_AMD64 !IsStructReturnKind(m_ReturnKind); @@ -1024,6 +1026,8 @@ void GcInfoEncoder::Build() GCINFO_WRITE(m_Info1, 0, 1, FlagsSize); // Slim encoding #if defined(TARGET_LOONGARCH64) assert(m_StackBaseRegister == 22 || 3 == m_StackBaseRegister); +#elif defined(TARGET_RISCV64) + assert(m_StackBaseRegister == 8 || 2 == m_StackBaseRegister); #endif GCINFO_WRITE(m_Info1, (m_StackBaseRegister == NO_STACK_BASE_REGISTER) ? 0 : 1, 1, FlagsSize); @@ -1039,11 +1043,13 @@ void GcInfoEncoder::Build() GCINFO_WRITE(m_Info1, m_contextParamType, 2, FlagsSize); #if defined(TARGET_LOONGARCH64) assert(m_StackBaseRegister == 22 || 3 == m_StackBaseRegister); +#elif defined(TARGET_RISCV64) + assert(m_StackBaseRegister == 8 || 2 == m_StackBaseRegister); #endif GCINFO_WRITE(m_Info1, ((m_StackBaseRegister != NO_STACK_BASE_REGISTER) ? 1 : 0), 1, FlagsSize); #ifdef TARGET_AMD64 GCINFO_WRITE(m_Info1, (m_WantsReportOnlyLeaf ? 1 : 0), 1, FlagsSize); -#elif defined(TARGET_ARM) || defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64) +#elif defined(TARGET_ARM) || defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64) GCINFO_WRITE(m_Info1, (m_HasTailCalls ? 1 : 0), 1, FlagsSize); #endif // TARGET_AMD64 GCINFO_WRITE(m_Info1, ((m_SizeOfEditAndContinuePreservedArea != NO_SIZE_OF_EDIT_AND_CONTINUE_PRESERVED_AREA) ? 1 : 0), 1, FlagsSize); @@ -1129,6 +1135,8 @@ void GcInfoEncoder::Build() { #if defined(TARGET_LOONGARCH64) assert(m_StackBaseRegister == 22 || 3 == m_StackBaseRegister); +#elif defined(TARGET_RISCV64) + assert(m_StackBaseRegister == 8 || 2 == m_StackBaseRegister); #endif GCINFO_WRITE_VARL_U(m_Info1, NORMALIZE_STACK_BASE_REGISTER(m_StackBaseRegister), STACK_BASE_REGISTER_ENCBASE, StackBaseSize); } diff --git a/src/coreclr/inc/clrconfigvalues.h b/src/coreclr/inc/clrconfigvalues.h index 9dd74d914ad746..e1c0669210b69c 100644 --- a/src/coreclr/inc/clrconfigvalues.h +++ b/src/coreclr/inc/clrconfigvalues.h @@ -293,7 +293,12 @@ CONFIG_DWORD_INFO(INTERNAL_JitDebuggable, W("JitDebuggable"), 0, "") #define INTERNAL_JitEnableNoWayAssert_Default 1 #endif RETAIL_CONFIG_DWORD_INFO(INTERNAL_JitEnableNoWayAssert, W("JitEnableNoWayAssert"), INTERNAL_JitEnableNoWayAssert_Default, "") + +#if defined(TARGET_RISCV64) +RETAIL_CONFIG_DWORD_INFO(UNSUPPORTED_JitFramed, W("JitFramed"), 1, "Forces EBP frames") +#else RETAIL_CONFIG_DWORD_INFO(UNSUPPORTED_JitFramed, W("JitFramed"), 0, "Forces EBP frames") +#endif // TARGET_RISCV64 CONFIG_DWORD_INFO(INTERNAL_JitThrowOnAssertionFailure, W("JitThrowOnAssertionFailure"), 0, "Throw managed exception on assertion failures during JIT instead of failfast") CONFIG_DWORD_INFO(INTERNAL_JitGCStress, W("JitGCStress"), 0, "GC stress mode for jit") CONFIG_DWORD_INFO(INTERNAL_JitHeartbeat, W("JitHeartbeat"), 0, "") @@ -737,12 +742,12 @@ RETAIL_CONFIG_DWORD_INFO(INTERNAL_GDBJitEmitDebugFrame, W("GDBJitEmitDebugFrame" // // Hardware Intrinsic ISAs; keep in sync with jitconfigvalues.h // -#if defined(TARGET_LOONGARCH64) +#if defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64) //TODO: should implement LoongArch64's features. RETAIL_CONFIG_DWORD_INFO(EXTERNAL_EnableHWIntrinsic, W("EnableHWIntrinsic"), 0, "Allows Base+ hardware intrinsics to be disabled") #else RETAIL_CONFIG_DWORD_INFO(EXTERNAL_EnableHWIntrinsic, W("EnableHWIntrinsic"), 1, "Allows Base+ hardware intrinsics to be disabled") -#endif // defined(TARGET_LOONGARCH64) +#endif // defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64) #if defined(TARGET_AMD64) || defined(TARGET_X86) RETAIL_CONFIG_DWORD_INFO(EXTERNAL_EnableAES, W("EnableAES"), 1, "Allows AES+ hardware intrinsics to be disabled") diff --git a/src/coreclr/inc/clrnt.h b/src/coreclr/inc/clrnt.h index 0bba92c7e3a2f9..3da9a6abf56787 100644 --- a/src/coreclr/inc/clrnt.h +++ b/src/coreclr/inc/clrnt.h @@ -1084,4 +1084,62 @@ RtlVirtualUnwind( #endif // TARGET_LOONGARCH64 +#ifdef TARGET_RISCV64 +#include "daccess.h" + +#define UNW_FLAG_NHANDLER 0x0 /* any handler */ +#define UNW_FLAG_EHANDLER 0x1 /* filter handler */ +#define UNW_FLAG_UHANDLER 0x2 /* unwind handler */ + +// This function returns the RVA of the end of the function (exclusive, so one byte after the actual end) +// using the unwind info on ARM64. (see ExternalAPIs\Win9CoreSystem\inc\winnt.h) +FORCEINLINE +ULONG64 +RtlpGetFunctionEndAddress ( + _In_ PT_RUNTIME_FUNCTION FunctionEntry, + _In_ ULONG64 ImageBase + ) +{ + // TODO RISCV64 + ULONG64 FunctionLength; + + FunctionLength = FunctionEntry->UnwindData; + if ((FunctionLength & 3) != 0) { + FunctionLength = (FunctionLength >> 2) & 0x7ff; + } else { + memcpy(&FunctionLength, (void*)(ImageBase + FunctionLength), sizeof(UINT32)); + FunctionLength &= 0x3ffff; + } + + return FunctionEntry->BeginAddress + 4 * FunctionLength; +} + +#define RUNTIME_FUNCTION__BeginAddress(FunctionEntry) ((FunctionEntry)->BeginAddress) +#define RUNTIME_FUNCTION__SetBeginAddress(FunctionEntry,address) ((FunctionEntry)->BeginAddress = (address)) + +#define RUNTIME_FUNCTION__EndAddress(FunctionEntry, ImageBase) (RtlpGetFunctionEndAddress(FunctionEntry, (ULONG64)(ImageBase))) + +#define RUNTIME_FUNCTION__SetUnwindInfoAddress(prf,address) do { (prf)->UnwindData = (address); } while (0) + +typedef struct _UNWIND_INFO { + // dummy +} UNWIND_INFO, *PUNWIND_INFO; + +EXTERN_C +NTSYSAPI +PEXCEPTION_ROUTINE +NTAPI +RtlVirtualUnwind( + IN ULONG HandlerType, + IN ULONG64 ImageBase, + IN ULONG64 ControlPc, + IN PRUNTIME_FUNCTION FunctionEntry, + IN OUT PCONTEXT ContextRecord, + OUT PVOID *HandlerData, + OUT PULONG64 EstablisherFrame, + IN OUT PKNONVOLATILE_CONTEXT_POINTERS ContextPointers OPTIONAL + ); + +#endif // TARGET_RISCV64 + #endif // CLRNT_H_ diff --git a/src/coreclr/inc/cordebuginfo.h b/src/coreclr/inc/cordebuginfo.h index 35e0eca6fd7894..01780570570e48 100644 --- a/src/coreclr/inc/cordebuginfo.h +++ b/src/coreclr/inc/cordebuginfo.h @@ -179,6 +179,40 @@ class ICorDebugInfo REGNUM_S7, REGNUM_S8, REGNUM_PC, +#elif TARGET_RISCV64 + REGNUM_R0, + REGNUM_RA, + REGNUM_SP, + REGNUM_GP, + REGNUM_TP, + REGNUM_T0, + REGNUM_T1, + REGNUM_T2, + REGNUM_S0, + REGNUM_S1, + REGNUM_A0, + REGNUM_A1, + REGNUM_A2, + REGNUM_A3, + REGNUM_A4, + REGNUM_A5, + REGNUM_A6, + REGNUM_A7, + REGNUM_S2, + REGNUM_S3, + REGNUM_S4, + REGNUM_S5, + REGNUM_S6, + REGNUM_S7, + REGNUM_S8, + REGNUM_S9, + REGNUM_S10, + REGNUM_S11, + REGNUM_T3, + REGNUM_T4, + REGNUM_T5, + REGNUM_T6, + REGNUM_PC, #else PORTABILITY_WARNING("Register numbers not defined on this platform") #endif @@ -201,6 +235,8 @@ class ICorDebugInfo //Nothing to do here. FP is already alloted. #elif TARGET_LOONGARCH64 //Nothing to do here. FP is already alloted. +#elif TARGET_RISCV64 + //Nothing to do here. FP is already alloted. #else // RegNum values should be properly defined for this platform REGNUM_FP = 0, diff --git a/src/coreclr/inc/crosscomp.h b/src/coreclr/inc/crosscomp.h index 7353e8cc6b770d..12adb32cc409db 100644 --- a/src/coreclr/inc/crosscomp.h +++ b/src/coreclr/inc/crosscomp.h @@ -517,6 +517,148 @@ typedef struct _T_KNONVOLATILE_CONTEXT_POINTERS { PDWORD64 F31; } T_KNONVOLATILE_CONTEXT_POINTERS, *PT_KNONVOLATILE_CONTEXT_POINTERS; +#elif defined(HOST_AMD64) && defined(TARGET_RISCV64) // Host amd64 managing RISCV64 related code + +#ifndef CROSS_COMPILE +#define CROSS_COMPILE +#endif + +// +// 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 RISCV64_MAX_BREAKPOINTS 8 +#define RISCV64_MAX_WATCHPOINTS 2 + +#define CONTEXT_UNWOUND_TO_CALL 0x20000000 + +typedef struct DECLSPEC_ALIGN(16) _T_CONTEXT { + + // + // Control flags. + // + + /* +0x000 */ DWORD ContextFlags; + + // + // Integer registers + // + DWORD64 R0; + DWORD64 Ra; + DWORD64 Sp; + DWORD64 Gp; + DWORD64 Tp; + DWORD64 T0; + DWORD64 T1; + DWORD64 T2; + DWORD64 Fp; + DWORD64 S1; + DWORD64 A0; + DWORD64 A1; + DWORD64 A2; + DWORD64 A3; + DWORD64 A4; + DWORD64 A5; + DWORD64 A6; + DWORD64 A7; + DWORD64 S2; + DWORD64 S3; + DWORD64 S4; + DWORD64 S5; + DWORD64 S6; + DWORD64 S7; + DWORD64 S8; + DWORD64 S9; + DWORD64 S10; + DWORD64 S11; + DWORD64 T3; + DWORD64 T4; + DWORD64 T5; + DWORD64 T6; + DWORD64 Pc; + + // + // Floating Point Registers + // + //TODO-RISCV64: support the SIMD. + ULONGLONG F[32]; + DWORD Fcsr; +} T_CONTEXT, *PT_CONTEXT; + +// _IMAGE_RISCV64_RUNTIME_FUNCTION_ENTRY (see ExternalAPIs\Win9CoreSystem\inc\winnt.h) +typedef struct _T_RUNTIME_FUNCTION { + DWORD BeginAddress; + union { + DWORD UnwindData; + struct { + DWORD Flag : 2; + DWORD FunctionLength : 11; + DWORD RegF : 3; + DWORD RegI : 4; + DWORD H : 1; + DWORD CR : 2; + DWORD FrameSize : 9; + } PackedUnwindData; + }; +} T_RUNTIME_FUNCTION, *PT_RUNTIME_FUNCTION; + +// +// Define exception dispatch context structure. +// + +typedef struct _T_DISPATCHER_CONTEXT { + DWORD64 ControlPc; + DWORD64 ImageBase; + PT_RUNTIME_FUNCTION FunctionEntry; + DWORD64 EstablisherFrame; + DWORD64 TargetPc; + PCONTEXT ContextRecord; + PEXCEPTION_ROUTINE LanguageHandler; + PVOID HandlerData; + PVOID HistoryTable; + DWORD ScopeIndex; + BOOLEAN ControlPcIsUnwound; + PBYTE NonVolatileRegisters; +} T_DISPATCHER_CONTEXT, *PT_DISPATCHER_CONTEXT; + +// +// Nonvolatile context pointer record. +// + +typedef struct _T_KNONVOLATILE_CONTEXT_POINTERS { + + PDWORD64 S1; + PDWORD64 S2; + PDWORD64 S3; + PDWORD64 S4; + PDWORD64 S5; + PDWORD64 S6; + PDWORD64 S7; + PDWORD64 S8; + PDWORD64 S9; + PDWORD64 S10; + PDWORD64 S11; + PDWORD64 Fp; + PDWORD64 Gp; + PDWORD64 Tp; + PDWORD64 Ra; + + PDWORD64 F8; + PDWORD64 F9; + PDWORD64 F18; + PDWORD64 F19; + PDWORD64 F20; + PDWORD64 F21; + PDWORD64 F22; + PDWORD64 F23; + PDWORD64 F24; + PDWORD64 F25; + PDWORD64 F26; + PDWORD64 F27; +} T_KNONVOLATILE_CONTEXT_POINTERS, *PT_KNONVOLATILE_CONTEXT_POINTERS; + #else #define T_CONTEXT CONTEXT diff --git a/src/coreclr/inc/eetwain.h b/src/coreclr/inc/eetwain.h index 5b20a4ef3a2365..bb5bf893b99ee7 100644 --- a/src/coreclr/inc/eetwain.h +++ b/src/coreclr/inc/eetwain.h @@ -211,9 +211,9 @@ virtual bool UnwindStackFrame(PREGDISPLAY pContext, virtual bool IsGcSafe(EECodeInfo *pCodeInfo, DWORD dwRelOffset) = 0; -#if defined(TARGET_ARM) || defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64) +#if defined(TARGET_ARM) || defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64) virtual bool HasTailCalls(EECodeInfo *pCodeInfo) = 0; -#endif // TARGET_ARM || TARGET_ARM64 || TARGET_LOONGARCH64 +#endif // TARGET_ARM || TARGET_ARM64 || TARGET_LOONGARCH64 || TARGET_RISCV64 #if defined(TARGET_AMD64) && defined(_DEBUG) /* @@ -455,10 +455,10 @@ virtual bool IsGcSafe( EECodeInfo *pCodeInfo, DWORD dwRelOffset); -#if defined(TARGET_ARM) || defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64) +#if defined(TARGET_ARM) || defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64) virtual bool HasTailCalls(EECodeInfo *pCodeInfo); -#endif // TARGET_ARM || TARGET_ARM64 || TARGET_LOONGARCH64 +#endif // TARGET_ARM || TARGET_ARM64 || TARGET_LOONGARCH64 || defined(TARGET_RISCV64) #if defined(TARGET_AMD64) && defined(_DEBUG) /* diff --git a/src/coreclr/inc/eexcp.h b/src/coreclr/inc/eexcp.h index 2de046d70dec8a..fb7bccbe073469 100644 --- a/src/coreclr/inc/eexcp.h +++ b/src/coreclr/inc/eexcp.h @@ -117,7 +117,7 @@ inline BOOL IsDuplicateClause(EE_ILEXCEPTION_CLAUSE* pEHClause) return pEHClause->Flags & COR_ILEXCEPTION_CLAUSE_DUPLICATED; } -#if defined(TARGET_AMD64) || defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64) +#if defined(TARGET_AMD64) || defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64) // Finally is the only EH construct that can be part of the execution as being fall-through. // // "Cloned" finally is a construct that represents a finally block that is used as @@ -139,7 +139,7 @@ inline BOOL IsClonedFinally(EE_ILEXCEPTION_CLAUSE* pEHClause) (pEHClause->TryStartPC == pEHClause->HandlerStartPC) && IsFinally(pEHClause) && IsDuplicateClause(pEHClause)); } -#endif // defined(TARGET_AMD64) || defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64) +#endif // defined(TARGET_AMD64) || defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64) #endif // __eexcp_h__ diff --git a/src/coreclr/inc/gcinfodecoder.h b/src/coreclr/inc/gcinfodecoder.h index 93bdd33ed3973e..f65cb5846bfbf5 100644 --- a/src/coreclr/inc/gcinfodecoder.h +++ b/src/coreclr/inc/gcinfodecoder.h @@ -216,7 +216,7 @@ enum GcInfoDecoderFlags DECODE_EDIT_AND_CONTINUE = 0x800, DECODE_REVERSE_PINVOKE_VAR = 0x1000, DECODE_RETURN_KIND = 0x2000, -#if defined(TARGET_ARM) || defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64) +#if defined(TARGET_ARM) || defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64) DECODE_HAS_TAILCALLS = 0x4000, #endif // TARGET_ARM || TARGET_ARM64 || TARGET_LOONGARCH64 }; @@ -235,7 +235,7 @@ enum GcInfoHeaderFlags GC_INFO_HAS_STACK_BASE_REGISTER = 0x40, #ifdef TARGET_AMD64 GC_INFO_WANTS_REPORT_ONLY_LEAF = 0x80, -#elif defined(TARGET_ARM) || defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64) +#elif defined(TARGET_ARM) || defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64) GC_INFO_HAS_TAILCALLS = 0x80, #endif // TARGET_AMD64 GC_INFO_HAS_EDIT_AND_CONTINUE_INFO = 0x100, @@ -539,9 +539,9 @@ class GcInfoDecoder bool HasMethodTableGenericsInstContext(); bool GetIsVarArg(); bool WantsReportOnlyLeaf(); -#if defined(TARGET_ARM) || defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64) +#if defined(TARGET_ARM) || defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64) bool HasTailCalls(); -#endif // TARGET_ARM || TARGET_ARM64 || TARGET_LOONGARCH64 +#endif // TARGET_ARM || TARGET_ARM64 || TARGET_LOONGARCH64 || defined(TARGET_RISCV64) ReturnKind GetReturnKind(); UINT32 GetCodeLength(); UINT32 GetStackBaseRegister(); @@ -567,7 +567,7 @@ class GcInfoDecoder bool m_GenericSecretParamIsMT; #ifdef TARGET_AMD64 bool m_WantsReportOnlyLeaf; -#elif defined(TARGET_ARM) || defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64) +#elif defined(TARGET_ARM) || defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64) bool m_HasTailCalls; #endif // TARGET_AMD64 INT32 m_GSCookieStackSlot; diff --git a/src/coreclr/inc/gcinfoencoder.h b/src/coreclr/inc/gcinfoencoder.h index 490aacf8ede25c..ea71e14d8dbfe0 100644 --- a/src/coreclr/inc/gcinfoencoder.h +++ b/src/coreclr/inc/gcinfoencoder.h @@ -442,7 +442,7 @@ class GcInfoEncoder // instead of once for each live function/funclet on the stack. // Called only by RyuJIT (not JIT64) void SetWantsReportOnlyLeaf(); -#elif defined(TARGET_ARM) || defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64) +#elif defined(TARGET_ARM) || defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64) void SetHasTailCalls(); #endif // TARGET_AMD64 @@ -494,7 +494,7 @@ class GcInfoEncoder bool m_IsVarArg; #if defined(TARGET_AMD64) bool m_WantsReportOnlyLeaf; -#elif defined(TARGET_ARM) || defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64) +#elif defined(TARGET_ARM) || defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64) bool m_HasTailCalls; #endif // TARGET_AMD64 INT32 m_GSCookieStackSlot; diff --git a/src/coreclr/inc/gcinfotypes.h b/src/coreclr/inc/gcinfotypes.h index 57cae4d264b719..33759d14d949ef 100644 --- a/src/coreclr/inc/gcinfotypes.h +++ b/src/coreclr/inc/gcinfotypes.h @@ -156,7 +156,7 @@ struct GcStackSlot // 10 RT_ByRef // 11 RT_Unset -#elif defined(TARGET_AMD64) || defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64) +#elif defined(TARGET_AMD64) || defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64) // Slim Header: @@ -829,6 +829,63 @@ void FASTCALL decodeCallPattern(int pattern, #define LIVESTATE_RLE_RUN_ENCBASE 2 #define LIVESTATE_RLE_SKIP_ENCBASE 4 +#elif defined(TARGET_RISCV64) +#ifndef TARGET_POINTER_SIZE +#define TARGET_POINTER_SIZE 8 // equal to sizeof(void*) and the managed pointer size in bytes for this target +#endif +#define NUM_NORM_CODE_OFFSETS_PER_CHUNK (64) +#define NUM_NORM_CODE_OFFSETS_PER_CHUNK_LOG2 (6) +#define NORMALIZE_STACK_SLOT(x) ((x)>>3) // GC Pointers are 8-bytes aligned +#define DENORMALIZE_STACK_SLOT(x) ((x)<<3) +#define NORMALIZE_CODE_LENGTH(x) ((x)>>2) // All Instructions are 4 bytes long +#define DENORMALIZE_CODE_LENGTH(x) ((x)<<2) +#define NORMALIZE_STACK_BASE_REGISTER(x) ((x)^8) // Encode Frame pointer X8 as zero +#define DENORMALIZE_STACK_BASE_REGISTER(x) ((x)^8) +#define NORMALIZE_SIZE_OF_STACK_AREA(x) ((x)>>3) +#define DENORMALIZE_SIZE_OF_STACK_AREA(x) ((x)<<3) +#define CODE_OFFSETS_NEED_NORMALIZATION 0 +#define NORMALIZE_CODE_OFFSET(x) (x) // Instructions are 4 bytes long, but the safe-point +#define DENORMALIZE_CODE_OFFSET(x) (x) // offsets are encoded with a -1 adjustment. +#define NORMALIZE_REGISTER(x) (x) +#define DENORMALIZE_REGISTER(x) (x) +#define NORMALIZE_NUM_SAFE_POINTS(x) (x) +#define DENORMALIZE_NUM_SAFE_POINTS(x) (x) +#define NORMALIZE_NUM_INTERRUPTIBLE_RANGES(x) (x) +#define DENORMALIZE_NUM_INTERRUPTIBLE_RANGES(x) (x) + +#define PSP_SYM_STACK_SLOT_ENCBASE 6 +#define GENERICS_INST_CONTEXT_STACK_SLOT_ENCBASE 6 +#define SECURITY_OBJECT_STACK_SLOT_ENCBASE 6 +#define GS_COOKIE_STACK_SLOT_ENCBASE 6 +#define CODE_LENGTH_ENCBASE 8 +#define SIZE_OF_RETURN_KIND_IN_SLIM_HEADER 2 +#define SIZE_OF_RETURN_KIND_IN_FAT_HEADER 4 +#define STACK_BASE_REGISTER_ENCBASE 2 // TODO for RISCV64 +// FP encoded as 0, SP as 2?? +#define SIZE_OF_STACK_AREA_ENCBASE 3 +#define SIZE_OF_EDIT_AND_CONTINUE_PRESERVED_AREA_ENCBASE 4 +#define SIZE_OF_EDIT_AND_CONTINUE_FIXED_STACK_FRAME_ENCBASE 4 +#define REVERSE_PINVOKE_FRAME_ENCBASE 6 +#define NUM_REGISTERS_ENCBASE 3 +#define NUM_STACK_SLOTS_ENCBASE 2 +#define NUM_UNTRACKED_SLOTS_ENCBASE 1 +#define NORM_PROLOG_SIZE_ENCBASE 5 +#define NORM_EPILOG_SIZE_ENCBASE 3 +#define NORM_CODE_OFFSET_DELTA_ENCBASE 3 +#define INTERRUPTIBLE_RANGE_DELTA1_ENCBASE 6 +#define INTERRUPTIBLE_RANGE_DELTA2_ENCBASE 6 +#define REGISTER_ENCBASE 3 +#define REGISTER_DELTA_ENCBASE 2 +#define STACK_SLOT_ENCBASE 6 +#define STACK_SLOT_DELTA_ENCBASE 4 +#define NUM_SAFE_POINTS_ENCBASE 3 +#define NUM_INTERRUPTIBLE_RANGES_ENCBASE 1 +#define NUM_EH_CLAUSES_ENCBASE 2 +#define POINTER_SIZE_ENCBASE 3 +#define LIVESTATE_RLE_RUN_ENCBASE 2 +#define LIVESTATE_RLE_SKIP_ENCBASE 4 + + #else #ifndef TARGET_X86 diff --git a/src/coreclr/inc/jithelpers.h b/src/coreclr/inc/jithelpers.h index 601588f98167e8..11cd8e36b8283b 100644 --- a/src/coreclr/inc/jithelpers.h +++ b/src/coreclr/inc/jithelpers.h @@ -325,7 +325,7 @@ JITHELPER(CORINFO_HELP_GVMLOOKUP_FOR_SLOT, NULL, CORINFO_HELP_SIG_NO_ALIGN_STUB) -#if !defined(TARGET_ARM64) && !defined(TARGET_LOONGARCH64) +#if !defined(TARGET_ARM64) && !defined(TARGET_LOONGARCH64) && !defined(TARGET_RISCV64) JITHELPER(CORINFO_HELP_STACK_PROBE, JIT_StackProbe, CORINFO_HELP_SIG_REG_ONLY) #else JITHELPER(CORINFO_HELP_STACK_PROBE, NULL, CORINFO_HELP_SIG_UNDEF) diff --git a/src/coreclr/inc/pedecoder.h b/src/coreclr/inc/pedecoder.h index 17baa6483079cb..4ada5241163b7c 100644 --- a/src/coreclr/inc/pedecoder.h +++ b/src/coreclr/inc/pedecoder.h @@ -87,6 +87,8 @@ inline CHECK CheckOverflow(RVA value1, COUNT_T value2) #define IMAGE_FILE_MACHINE_NATIVE IMAGE_FILE_MACHINE_POWERPC #elif defined(TARGET_S390X) #define IMAGE_FILE_MACHINE_NATIVE IMAGE_FILE_MACHINE_UNKNOWN +#elif defined(TARGET_RISCV64) +#define IMAGE_FILE_MACHINE_NATIVE IMAGE_FILE_MACHINE_RISCV64 #else #error "port me" #endif diff --git a/src/coreclr/inc/regdisp.h b/src/coreclr/inc/regdisp.h index b6ccc87ff6d876..e7a230d2dab834 100644 --- a/src/coreclr/inc/regdisp.h +++ b/src/coreclr/inc/regdisp.h @@ -210,6 +210,28 @@ typedef struct _Loongarch64VolatileContextPointer } Loongarch64VolatileContextPointer; #endif +#if defined(TARGET_RISCV64) +typedef struct _Riscv64VolatileContextPointer +{ + PDWORD64 R0; + PDWORD64 A0; + PDWORD64 A1; + PDWORD64 A2; + PDWORD64 A3; + PDWORD64 A4; + PDWORD64 A5; + PDWORD64 A6; + PDWORD64 A7; + PDWORD64 T0; + PDWORD64 T1; + PDWORD64 T2; + PDWORD64 T3; + PDWORD64 T4; + PDWORD64 T5; + PDWORD64 T6; +} Riscv64VolatileContextPointer; +#endif + struct REGDISPLAY : public REGDISPLAY_BASE { #ifdef TARGET_ARM64 Arm64VolatileContextPointer volatileCurrContextPointers; @@ -219,6 +241,10 @@ struct REGDISPLAY : public REGDISPLAY_BASE { Loongarch64VolatileContextPointer volatileCurrContextPointers; #endif +#ifdef TARGET_RISCV64 + Riscv64VolatileContextPointer volatileCurrContextPointers; +#endif + REGDISPLAY() { // Initialize @@ -329,6 +355,8 @@ inline LPVOID GetRegdisplayReturnValue(REGDISPLAY *display) return (LPVOID)display->pCurrentContext->Eax; #elif defined(TARGET_LOONGARCH64) return (LPVOID)display->pCurrentContext->A0; +#elif defined(TARGET_RISCV64) + return (LPVOID)display->pCurrentContext->A0; #else PORTABILITY_ASSERT("GetRegdisplayReturnValue NYI for this platform (Regdisp.h)"); return NULL; @@ -397,7 +425,24 @@ inline void FillContextPointers(PT_KNONVOLATILE_CONTEXT_POINTERS pCtxPtrs, PT_CO { *(&pCtxPtrs->Edi + i) = (&pCtx->Edi + i); } -#else // TARGET_X86 +#elif defined(TARGET_RISCV64) // TARGET_X86 + // *(&pCtxPtrs->S0) = &pCtx->S0; + *(&pCtxPtrs->S1) = &pCtx->S1; + *(&pCtxPtrs->S2) = &pCtx->S2; + *(&pCtxPtrs->S3) = &pCtx->S3; + *(&pCtxPtrs->S4) = &pCtx->S4; + *(&pCtxPtrs->S5) = &pCtx->S5; + *(&pCtxPtrs->S6) = &pCtx->S6; + *(&pCtxPtrs->S7) = &pCtx->S7; + *(&pCtxPtrs->S8) = &pCtx->S8; + *(&pCtxPtrs->S9) = &pCtx->S9; + *(&pCtxPtrs->S10) = &pCtx->S10; + *(&pCtxPtrs->S11) = &pCtx->S11; + *(&pCtxPtrs->Gp) = &pCtx->Gp; + *(&pCtxPtrs->Tp) = &pCtx->Tp; + *(&pCtxPtrs->Fp) = &pCtx->Fp; + *(&pCtxPtrs->Ra) = &pCtx->Ra; +#else // TARGET_RISCV64 PORTABILITY_ASSERT("FillContextPointers"); #endif // _TARGET_???_ (ELSE) } @@ -488,7 +533,23 @@ inline void FillRegDisplay(const PREGDISPLAY pRD, PT_CONTEXT pctx, PT_CONTEXT pC pRD->volatileCurrContextPointers.T7 = &pctx->T7; pRD->volatileCurrContextPointers.T8 = &pctx->T8; pRD->volatileCurrContextPointers.X0 = &pctx->X0; -#endif // TARGET_LOONGARCH64 +#elif defined(TARGET_RISCV64) // TARGET_LOONGARCH64 + pRD->volatileCurrContextPointers.A0 = &pctx->A0; + pRD->volatileCurrContextPointers.A1 = &pctx->A1; + pRD->volatileCurrContextPointers.A2 = &pctx->A2; + pRD->volatileCurrContextPointers.A3 = &pctx->A3; + pRD->volatileCurrContextPointers.A4 = &pctx->A4; + pRD->volatileCurrContextPointers.A5 = &pctx->A5; + pRD->volatileCurrContextPointers.A6 = &pctx->A6; + pRD->volatileCurrContextPointers.A7 = &pctx->A7; + pRD->volatileCurrContextPointers.T0 = &pctx->T0; + pRD->volatileCurrContextPointers.T1 = &pctx->T1; + pRD->volatileCurrContextPointers.T2 = &pctx->T2; + pRD->volatileCurrContextPointers.T3 = &pctx->T3; + pRD->volatileCurrContextPointers.T4 = &pctx->T4; + pRD->volatileCurrContextPointers.T5 = &pctx->T5; + pRD->volatileCurrContextPointers.T6 = &pctx->T6; +#endif // TARGET_RISCV64 #ifdef DEBUG_REGDISPLAY pRD->_pThread = NULL; @@ -571,6 +632,9 @@ inline size_t * getRegAddr (unsigned regNum, PTR_CONTEXT regs) #elif defined(TARGET_LOONGARCH64) _ASSERTE(regNum < 32); return (size_t *)®s->R0 + regNum; +#elif defined(TARGET_RISCV64) + _ASSERTE(regNum < 32); + return (size_t *)®s->R0 + regNum; #else _ASSERTE(!"@TODO Port - getRegAddr (Regdisp.h)"); #endif diff --git a/src/coreclr/inc/switches.h b/src/coreclr/inc/switches.h index b040f3d681b212..8b8ee515ef30b9 100644 --- a/src/coreclr/inc/switches.h +++ b/src/coreclr/inc/switches.h @@ -53,7 +53,7 @@ #if defined(TARGET_X86) || defined(TARGET_ARM) #define USE_LAZY_PREFERRED_RANGE 0 -#elif defined(TARGET_AMD64) || defined(TARGET_ARM64) || defined(TARGET_S390X) || defined(TARGET_LOONGARCH64) || defined(TARGET_POWERPC64) +#elif defined(TARGET_AMD64) || defined(TARGET_ARM64) || defined(TARGET_S390X) || defined(TARGET_LOONGARCH64) || defined(TARGET_POWERPC64) || defined(TARGET_RISCV64) #if defined(HOST_UNIX) // In PAL we have a smechanism that reserves memory on start up that is diff --git a/src/coreclr/inc/targetosarch.h b/src/coreclr/inc/targetosarch.h index 9025a8608af0fc..fbe7806cf7d1c3 100644 --- a/src/coreclr/inc/targetosarch.h +++ b/src/coreclr/inc/targetosarch.h @@ -42,6 +42,7 @@ class TargetArchitecture static const bool IsArm32 = true; static const bool IsArmArch = true; static const bool IsLoongArch64 = false; + static const bool IsRiscv64 = false; #elif defined(TARGET_ARM64) static const bool IsX86 = false; static const bool IsX64 = false; @@ -49,6 +50,7 @@ class TargetArchitecture static const bool IsArm32 = false; static const bool IsArmArch = true; static const bool IsLoongArch64 = false; + static const bool IsRiscv64 = false; #elif defined(TARGET_AMD64) static const bool IsX86 = false; static const bool IsX64 = true; @@ -56,6 +58,7 @@ class TargetArchitecture static const bool IsArm32 = false; static const bool IsArmArch = false; static const bool IsLoongArch64 = false; + static const bool IsRiscv64 = false; #elif defined(TARGET_X86) static const bool IsX86 = true; static const bool IsX64 = false; @@ -63,6 +66,7 @@ class TargetArchitecture static const bool IsArm32 = false; static const bool IsArmArch = false; static const bool IsLoongArch64 = false; + static const bool IsRiscv64 = false; #elif defined(TARGET_LOONGARCH64) static const bool IsX86 = false; static const bool IsX64 = false; @@ -70,6 +74,15 @@ class TargetArchitecture static const bool IsArm32 = false; static const bool IsArmArch = false; static const bool IsLoongArch64 = true; + static const bool IsRiscv64 = false; +#elif defined(TARGET_RISCV64) + static const bool IsX86 = false; + static const bool IsX64 = false; + static const bool IsArm64 = false; + static const bool IsArm32 = false; + static const bool IsArmArch = false; + static const bool IsLoongArch64 = false; + static const bool IsRiscv64 = true; #else #error Unknown architecture #endif diff --git a/src/coreclr/inc/volatile.h b/src/coreclr/inc/volatile.h index 177c4932166c69..030638c2ef2a9a 100644 --- a/src/coreclr/inc/volatile.h +++ b/src/coreclr/inc/volatile.h @@ -68,8 +68,8 @@ #error The Volatile type is currently only defined for Visual C++ and GNU C++ #endif -#if defined(__GNUC__) && !defined(HOST_X86) && !defined(HOST_AMD64) && !defined(HOST_ARM) && !defined(HOST_ARM64) && !defined(HOST_LOONGARCH64) && !defined(HOST_RISCV64) && !defined(HOST_S390X) && !defined(HOST_POWERPC64) -#error The Volatile type is currently only defined for GCC when targeting x86, AMD64, ARM, ARM64, LOONGARCH64, RISCV64, PPC64LE, or S390X CPUs +#if defined(__GNUC__) && !defined(HOST_X86) && !defined(HOST_AMD64) && !defined(HOST_ARM) && !defined(HOST_ARM64) && !defined(HOST_LOONGARCH64) && !defined(HOST_RISCV64) && !defined(HOST_S390X) && !defined(HOST_POWERPC64) && !defined(HOST_RISCV64) +#error The Volatile type is currently only defined for GCC when targeting x86, AMD64, ARM, ARM64, LOONGARCH64, RISCV64, PPC64LE, S390X, or RISCV64 CPUs #endif #if defined(__GNUC__) diff --git a/src/coreclr/scripts/coreclr_arguments.py b/src/coreclr/scripts/coreclr_arguments.py index 400a2da00c2dc5..4bea9477b8391d 100644 --- a/src/coreclr/scripts/coreclr_arguments.py +++ b/src/coreclr/scripts/coreclr_arguments.py @@ -63,7 +63,7 @@ def __init__(self, self.require_built_core_root = require_built_core_root self.require_built_test_dir = require_built_test_dir - self.valid_arches = ["x64", "x86", "arm", "arm64", "loongarch64", "wasm"] + self.valid_arches = ["x64", "x86", "arm", "arm64", "loongarch64", "riscv64", "wasm"] self.valid_build_types = ["Debug", "Checked", "Release"] self.valid_host_os = ["windows", "osx", "linux", "illumos", "solaris", "browser", "android", "wasi"] diff --git a/src/coreclr/tools/superpmi/superpmi-shared/spmiutil.cpp b/src/coreclr/tools/superpmi/superpmi-shared/spmiutil.cpp index ff4dd582f3c1bf..676c490753924f 100644 --- a/src/coreclr/tools/superpmi/superpmi-shared/spmiutil.cpp +++ b/src/coreclr/tools/superpmi/superpmi-shared/spmiutil.cpp @@ -253,6 +253,8 @@ static SPMI_TARGET_ARCHITECTURE SpmiTargetArchitecture = SPMI_TARGET_ARCHITECTUR static SPMI_TARGET_ARCHITECTURE SpmiTargetArchitecture = SPMI_TARGET_ARCHITECTURE_ARM64; #elif defined(TARGET_LOONGARCH64) static SPMI_TARGET_ARCHITECTURE SpmiTargetArchitecture = SPMI_TARGET_ARCHITECTURE_LOONGARCH64; +#elif defined(TARGET_RISCV64) +static SPMI_TARGET_ARCHITECTURE SpmiTargetArchitecture = SPMI_TARGET_ARCHITECTURE_RISCV64; #else #error Unsupported architecture #endif diff --git a/src/coreclr/tools/superpmi/superpmi-shared/spmiutil.h b/src/coreclr/tools/superpmi/superpmi-shared/spmiutil.h index a97e8fab4b32e6..4b13202f5e2933 100644 --- a/src/coreclr/tools/superpmi/superpmi-shared/spmiutil.h +++ b/src/coreclr/tools/superpmi/superpmi-shared/spmiutil.h @@ -54,7 +54,8 @@ enum SPMI_TARGET_ARCHITECTURE SPMI_TARGET_ARCHITECTURE_AMD64, SPMI_TARGET_ARCHITECTURE_ARM64, SPMI_TARGET_ARCHITECTURE_ARM, - SPMI_TARGET_ARCHITECTURE_LOONGARCH64 + SPMI_TARGET_ARCHITECTURE_LOONGARCH64, + SPMI_TARGET_ARCHITECTURE_RISCV64 }; SPMI_TARGET_ARCHITECTURE GetSpmiTargetArchitecture(); @@ -67,7 +68,7 @@ inline bool IsSpmiTarget32Bit() inline bool IsSpmiTarget64Bit() { - return (GetSpmiTargetArchitecture() == SPMI_TARGET_ARCHITECTURE_AMD64) || (GetSpmiTargetArchitecture() == SPMI_TARGET_ARCHITECTURE_ARM64) || (GetSpmiTargetArchitecture() == SPMI_TARGET_ARCHITECTURE_LOONGARCH64); + return (GetSpmiTargetArchitecture() == SPMI_TARGET_ARCHITECTURE_AMD64) || (GetSpmiTargetArchitecture() == SPMI_TARGET_ARCHITECTURE_ARM64) || (GetSpmiTargetArchitecture() == SPMI_TARGET_ARCHITECTURE_LOONGARCH64) || (GetSpmiTargetArchitecture() == SPMI_TARGET_ARCHITECTURE_RISCV64); } inline size_t SpmiTargetPointerSize() diff --git a/src/coreclr/unwinder/riscv64/unwinder.cpp b/src/coreclr/unwinder/riscv64/unwinder.cpp index bec3a8da31b44d..53093f7e135880 100644 --- a/src/coreclr/unwinder/riscv64/unwinder.cpp +++ b/src/coreclr/unwinder/riscv64/unwinder.cpp @@ -9,4 +9,1466 @@ #include "unwinder.h" -#error "TODO-RISCV64: missing implementation" +typedef struct _RISCV64_KTRAP_FRAME { + +// +// Exception active indicator. +// +// 0 - interrupt frame. +// 1 - exception frame. +// 2 - service frame. +// + + /* +0x000 */ UCHAR ExceptionActive; // always valid + /* +0x001 */ UCHAR ContextFromKFramesUnwound; // set if KeContextFromKFrames created this frame + /* +0x002 */ UCHAR DebugRegistersValid; // always valid + /* +0x003 */ union { + UCHAR PreviousMode; // system services only + UCHAR PreviousIrql; // interrupts only + }; + +// +// Page fault information (page faults only) +// Previous trap frame address (system services only) +// +// Organized this way to allow first couple words to be used +// for scratch space in the general case +// + + /* +0x004 */ ULONG FaultStatus; // page faults only + /* +0x008 */ union { + ULONG64 FaultAddress; // page faults only + ULONG64 TrapFrame; // system services only + }; + +// +// The LOONGARCH architecture does not have an architectural trap frame. On +// an exception or interrupt, the processor switches to an +// exception-specific processor mode in which at least the RA and SP +// registers are banked. Software is responsible for preserving +// registers which reflect the processor state in which the +// exception occurred rather than any intermediate processor modes. +// + +// +// Volatile floating point state is dynamically allocated; this +// pointer may be NULL if the FPU was not enabled at the time the +// trap was taken. +// + + /* +0x010 */ PVOID VfpState; + +// +// Volatile registers +// + ULONG64 R[15]; + ULONG64 Gp; + ULONG64 Tp; + ULONG64 Sp; + ULONG64 Fp; + ULONG64 Ra; + ULONG64 Pc; + +} RISCV64_KTRAP_FRAME, *PRISCV64_KTRAP_FRAME; + +typedef struct _RISCV64_VFP_STATE +{ + struct _RISCV64_VFP_STATE *Link; // link to next state entry + ULONG Fcsr; // FCSR register + ULONG64 F[32]; // All F registers (0-31) +} RISCV64_VFP_STATE, *PRISCV64_VFP_STATE, KRISCV64_VFP_STATE, *PKRISCV64_VFP_STATE; + +// +// Parameters describing the unwind codes. +// + +#define STATUS_UNWIND_UNSUPPORTED_VERSION STATUS_UNSUCCESSFUL +#define STATUS_UNWIND_NOT_IN_FUNCTION STATUS_UNSUCCESSFUL +#define STATUS_UNWIND_INVALID_SEQUENCE STATUS_UNSUCCESSFUL + +// +// Macros for accessing memory. These can be overridden if other code +// (in particular the debugger) needs to use them. + +#define MEMORY_READ_BYTE(params, addr) (*dac_cast(addr)) +#define MEMORY_READ_DWORD(params, addr) (*dac_cast(addr)) +#define MEMORY_READ_QWORD(params, addr) (*dac_cast(addr)) + +typedef struct _RISCV64_UNWIND_PARAMS +{ + PT_KNONVOLATILE_CONTEXT_POINTERS ContextPointers; +} RISCV64_UNWIND_PARAMS, *PRISCV64_UNWIND_PARAMS; + + +#define UNWIND_PARAMS_SET_TRAP_FRAME(Params, Address, Size) +#define UPDATE_CONTEXT_POINTERS(Params, RegisterNumber, Address) \ +do { \ + if (ARGUMENT_PRESENT(Params)) { \ + PT_KNONVOLATILE_CONTEXT_POINTERS ContextPointers = (Params)->ContextPointers; \ + if (ARGUMENT_PRESENT(ContextPointers)) { \ + if (RegisterNumber == 8) \ + ContextPointers->Fp = (PDWORD64)Address; \ + else if (RegisterNumber == 9) \ + ContextPointers->S1 = (PDWORD64)Address; \ + else if (RegisterNumber >= 18 && RegisterNumber <= 27) \ + (&ContextPointers->S2)[RegisterNumber - 18] = (PDWORD64)Address; \ + } \ + } \ +} while (0) + + +#define UPDATE_FP_CONTEXT_POINTERS(Params, RegisterNumber, Address) \ +do { \ + if (ARGUMENT_PRESENT(Params)) { \ + PT_KNONVOLATILE_CONTEXT_POINTERS ContextPointers = (Params)->ContextPointers; \ + if (ARGUMENT_PRESENT(ContextPointers)) { \ + if (RegisterNumber == 8) \ + ContextPointers->F8 = (PDWORD64)Address; \ + else if (RegisterNumber == 9) \ + ContextPointers->F9 = (PDWORD64)Address; \ + else if (RegisterNumber >= 18 && RegisterNumber <= 27) \ + (&ContextPointers->F18)[RegisterNumber - 18] = (PDWORD64)Address; \ + } \ + } \ +} while (0) + +#define VALIDATE_STACK_ADDRESS_EX(Params, Context, Address, DataSize, Alignment, OutStatus) +#define VALIDATE_STACK_ADDRESS(Params, Context, DataSize, Alignment, OutStatus) + +// +// Macros to clarify opcode parsing +// + +#define OPCODE_IS_END(Op) (((Op) & 0xfe) == 0xe4) + +// +// This table describes the size of each unwind code, in bytes +// + +static const BYTE UnwindCodeSizeTable[256] = +{ + 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, + 2,2,2,2,2,2,2,2, 2,2,2,2,2,2,2,2, 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, + 2,2,2,2,2,2,2,2, 2,2,2,2,2,2,2,2, 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, + 2,2,2,2,2,2,2,2, 3,2,2,2,3,2,2,2, 3,2,2,2,2,2,3,2, 3,2,3,2,3,2,2,2, + 4,1,3,1,1,1,1,1, 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1 +}; + +NTSTATUS +RtlpUnwindCustom( + __inout PT_CONTEXT ContextRecord, + _In_ BYTE Opcode, + _In_ PRISCV64_UNWIND_PARAMS UnwindParams + ) + +/*++ + +Routine Description: + + Handles custom unwinding operations involving machine-specific + frames. + +Arguments: + + ContextRecord - Supplies the address of a context record. + + Opcode - The opcode to decode. + + UnwindParams - Additional parameters shared with caller. + +Return Value: + + An NTSTATUS indicating either STATUS_SUCCESS if everything went ok, or + another status code if there were problems. + +--*/ + +{ + ULONG Fcsr; + ULONG RegIndex; + ULONG_PTR SourceAddress; + ULONG_PTR StartingSp; + NTSTATUS Status; + ULONG_PTR VfpStateAddress; + + StartingSp = ContextRecord->Sp; + Status = STATUS_SUCCESS; + + // + // The opcode describes the special-case stack + // + + switch (Opcode) + { + + // + // Trap frame case + // + + case 0xe8: // MSFT_OP_TRAP_FRAME: + + // + // Ensure there is enough valid space for the trap frame + // + + VALIDATE_STACK_ADDRESS(UnwindParams, ContextRecord, sizeof(RISCV64_KTRAP_FRAME), 16, &Status); + if (!NT_SUCCESS(Status)) { + return Status; + } + + // + // Restore R0-R14, and F0-F32 + // + SourceAddress = StartingSp + offsetof(RISCV64_KTRAP_FRAME, R); + for (RegIndex = 0; RegIndex < 15; RegIndex++) { + UPDATE_CONTEXT_POINTERS(UnwindParams, RegIndex, SourceAddress); +#ifdef __GNUC__ + *(&ContextRecord->R0 + RegIndex) = MEMORY_READ_QWORD(UnwindParams, SourceAddress); +#else + ContextRecord->R[RegIndex] = MEMORY_READ_QWORD(UnwindParams, SourceAddress); +#endif + SourceAddress += sizeof(ULONG_PTR); + } + + SourceAddress = StartingSp + offsetof(RISCV64_KTRAP_FRAME, VfpState); + VfpStateAddress = MEMORY_READ_QWORD(UnwindParams, SourceAddress); + if (VfpStateAddress != 0) { + + SourceAddress = VfpStateAddress + offsetof(KRISCV64_VFP_STATE, Fcsr); + Fcsr = MEMORY_READ_DWORD(UnwindParams, SourceAddress); + if (Fcsr != (ULONG)-1) { + + ContextRecord->Fcsr = Fcsr; + + SourceAddress = VfpStateAddress + offsetof(KRISCV64_VFP_STATE, F); + for (RegIndex = 0; RegIndex < 32; RegIndex++) { + UPDATE_FP_CONTEXT_POINTERS(UnwindParams, RegIndex, SourceAddress); + ContextRecord->F[RegIndex] = MEMORY_READ_QWORD(UnwindParams, SourceAddress); + SourceAddress += 2 * sizeof(ULONGLONG); + } + } + } + + // + // Restore SP, RA, PC, and the status registers + // + + //SourceAddress = StartingSp + offsetof(RISCV64_KTRAP_FRAME, Tp);//TP + //ContextRecord->Tp = MEMORY_READ_QWORD(UnwindParams, SourceAddress); + + SourceAddress = StartingSp + offsetof(RISCV64_KTRAP_FRAME, Sp); + ContextRecord->Sp = MEMORY_READ_QWORD(UnwindParams, SourceAddress); + + SourceAddress = StartingSp + offsetof(RISCV64_KTRAP_FRAME, Fp); + ContextRecord->Fp = MEMORY_READ_QWORD(UnwindParams, SourceAddress); + + SourceAddress = StartingSp + offsetof(RISCV64_KTRAP_FRAME, Ra); + ContextRecord->Ra = MEMORY_READ_QWORD(UnwindParams, SourceAddress); + + SourceAddress = StartingSp + offsetof(RISCV64_KTRAP_FRAME, Pc); + ContextRecord->Pc = MEMORY_READ_QWORD(UnwindParams, SourceAddress); + + // + // Set the trap frame and clear the unwound-to-call flag + // + + UNWIND_PARAMS_SET_TRAP_FRAME(UnwindParams, StartingSp, sizeof(RISCV64_KTRAP_FRAME)); + ContextRecord->ContextFlags &= ~CONTEXT_UNWOUND_TO_CALL; + break; + + // + // Context case + // + + case 0xea: // MSFT_OP_CONTEXT: + + // + // Ensure there is enough valid space for the full CONTEXT structure + // + + VALIDATE_STACK_ADDRESS(UnwindParams, ContextRecord, sizeof(CONTEXT), 16, &Status); + if (!NT_SUCCESS(Status)) { + return Status; + } + + // + // Restore R0-R23, and F0-F31 + // + + SourceAddress = StartingSp + offsetof(T_CONTEXT, R0); + for (RegIndex = 0; RegIndex < 23; RegIndex++) { + UPDATE_CONTEXT_POINTERS(UnwindParams, RegIndex, SourceAddress); +#ifdef __GNUC__ + *(&ContextRecord->R0 + RegIndex) = MEMORY_READ_QWORD(UnwindParams, SourceAddress); +#else + ContextRecord->R[RegIndex] = MEMORY_READ_QWORD(UnwindParams, SourceAddress); +#endif + SourceAddress += sizeof(ULONG_PTR); + } + + SourceAddress = StartingSp + offsetof(T_CONTEXT, F); + for (RegIndex = 0; RegIndex < 32; RegIndex++) { + UPDATE_FP_CONTEXT_POINTERS(UnwindParams, RegIndex, SourceAddress); + ContextRecord->F[RegIndex] = MEMORY_READ_QWORD(UnwindParams, SourceAddress); + SourceAddress += 2 * sizeof(ULONGLONG); + } + + // + // Restore SP, RA, PC, and the status registers + // + + SourceAddress = StartingSp + offsetof(T_CONTEXT, Fp); + ContextRecord->Fp = MEMORY_READ_QWORD(UnwindParams, SourceAddress); + + SourceAddress = StartingSp + offsetof(T_CONTEXT, Sp); + ContextRecord->Sp = MEMORY_READ_QWORD(UnwindParams, SourceAddress); + + SourceAddress = StartingSp + offsetof(T_CONTEXT, Pc); + ContextRecord->Pc = MEMORY_READ_QWORD(UnwindParams, SourceAddress); + + SourceAddress = StartingSp + offsetof(T_CONTEXT, Fcsr); + ContextRecord->Fcsr = MEMORY_READ_DWORD(UnwindParams, SourceAddress); + + // + // Inherit the unwound-to-call flag from this context + // + + SourceAddress = StartingSp + offsetof(T_CONTEXT, ContextFlags); + ContextRecord->ContextFlags &= ~CONTEXT_UNWOUND_TO_CALL; + ContextRecord->ContextFlags |= + MEMORY_READ_DWORD(UnwindParams, SourceAddress) & CONTEXT_UNWOUND_TO_CALL; + break; + + default: + return STATUS_UNSUCCESSFUL; + } + + return STATUS_SUCCESS; +} + +ULONG +RtlpComputeScopeSize( + _In_ ULONG_PTR UnwindCodePtr, + _In_ ULONG_PTR UnwindCodesEndPtr, + _In_ BOOLEAN IsEpilog, + _In_ PRISCV64_UNWIND_PARAMS UnwindParams + ) + +/*++ + +Routine Description: + + Computes the size of an prolog or epilog, in words. + +Arguments: + + UnwindCodePtr - Supplies a pointer to the start of the unwind + code sequence. + + UnwindCodesEndPtr - Supplies a pointer to the byte immediately + following the unwind code table, as described by the header. + + IsEpilog - Specifies TRUE if the scope describes an epilog, + or FALSE if it describes a prolog. + + UnwindParams - Additional parameters shared with caller. + +Return Value: + + The size of the scope described by the unwind codes, in halfword units. + +--*/ + +{ + ULONG ScopeSize; + BYTE Opcode; + + // + // Iterate through the unwind codes until we hit an end marker. + // While iterating, accumulate the total scope size. + // + + ScopeSize = 0; + Opcode = 0; + while (UnwindCodePtr < UnwindCodesEndPtr) { + Opcode = MEMORY_READ_BYTE(UnwindParams, UnwindCodePtr); + if (OPCODE_IS_END(Opcode)) { + break; + } + + UnwindCodePtr += UnwindCodeSizeTable[Opcode]; + ScopeSize++; + } + + // + // Epilogs have one extra instruction at the end that needs to be + // accounted for. + // + + if (IsEpilog) { + ScopeSize++; + } + + return ScopeSize; +} + +NTSTATUS +RtlpUnwindRestoreRegisterRange( + __inout PT_CONTEXT ContextRecord, + _In_ LONG SpOffset, + _In_ ULONG FirstRegister, + _In_ ULONG RegisterCount, + _In_ PRISCV64_UNWIND_PARAMS UnwindParams + ) + +/*++ + +Routine Description: + + Restores a series of integer registers from the stack. + +Arguments: + + ContextRecord - Supplies the address of a context record. + + SpOffset - Specifies a stack offset. Positive values are simply used + as a base offset. Negative values assume a predecrement behavior: + a 0 offset is used for restoration, but the absolute value of the + offset is added to the final Sp. + + FirstRegister - Specifies the index of the first register to restore. + + RegisterCount - Specifies the number of registers to restore. + + UnwindParams - Additional parameters shared with caller. + +Return Value: + + None. + +--*/ + +{ + ULONG_PTR CurAddress; + ULONG RegIndex; + NTSTATUS Status; + + // + // Compute the source address and validate it. + // + + CurAddress = ContextRecord->Sp; + if (SpOffset >= 0) { + CurAddress += SpOffset; + } + + Status = STATUS_SUCCESS; + VALIDATE_STACK_ADDRESS(UnwindParams, ContextRecord, 8 * RegisterCount, 8, &Status); + if (Status != STATUS_SUCCESS) { + return Status; + } + + // + // Restore the registers + // + for (RegIndex = 0; RegIndex < RegisterCount; RegIndex++) { + UPDATE_CONTEXT_POINTERS(UnwindParams, FirstRegister + RegIndex, CurAddress); +#ifdef __GNUC__ + *(&ContextRecord->R0 + FirstRegister + RegIndex) = MEMORY_READ_QWORD(UnwindParams, CurAddress); +#else + ContextRecord->R[FirstRegister + RegIndex] = MEMORY_READ_QWORD(UnwindParams, CurAddress); +#endif + CurAddress += 8; + } + if (SpOffset < 0) { + ContextRecord->Sp -= SpOffset; + } + + return STATUS_SUCCESS; +} + +NTSTATUS +RtlpUnwindRestoreFpRegisterRange( + __inout PT_CONTEXT ContextRecord, + _In_ LONG SpOffset, + _In_ ULONG FirstRegister, + _In_ ULONG RegisterCount, + _In_ PRISCV64_UNWIND_PARAMS UnwindParams + ) + +/*++ + +Routine Description: + + Restores a series of floating-point registers from the stack. + +Arguments: + + ContextRecord - Supplies the address of a context record. + + SpOffset - Specifies a stack offset. Positive values are simply used + as a base offset. Negative values assume a predecrement behavior: + a 0 offset is used for restoration, but the absolute value of the + offset is added to the final Sp. + + FirstRegister - Specifies the index of the first register to restore. + + RegisterCount - Specifies the number of registers to restore. + + UnwindParams - Additional parameters shared with caller. + +Return Value: + + None. + +--*/ + +{ + ULONG_PTR CurAddress; + ULONG RegIndex; + NTSTATUS Status; + + // + // Compute the source address and validate it. + // + + CurAddress = ContextRecord->Sp; + if (SpOffset >= 0) { + CurAddress += SpOffset; + } + + Status = STATUS_SUCCESS; + VALIDATE_STACK_ADDRESS(UnwindParams, ContextRecord, 8 * RegisterCount, 8, &Status); + if (Status != STATUS_SUCCESS) { + return Status; + } + + // + // Restore the registers + // + + for (RegIndex = 0; RegIndex < RegisterCount; RegIndex++) { + UPDATE_FP_CONTEXT_POINTERS(UnwindParams, FirstRegister + RegIndex, CurAddress); + ContextRecord->F[FirstRegister + RegIndex] = MEMORY_READ_QWORD(UnwindParams, CurAddress); + CurAddress += 8; + } + if (SpOffset < 0) { + ContextRecord->Sp -= SpOffset; + } + + return STATUS_SUCCESS; +} + +NTSTATUS +RtlpUnwindFunctionFull( + _In_ DWORD64 ControlPcRva, + _In_ ULONG_PTR ImageBase, + _In_ PT_RUNTIME_FUNCTION FunctionEntry, + __inout T_CONTEXT *ContextRecord, + _Out_ PDWORD64 EstablisherFrame, + __deref_opt_out_opt PEXCEPTION_ROUTINE *HandlerRoutine, + _Out_ PVOID *HandlerData, + _In_ PRISCV64_UNWIND_PARAMS UnwindParams + ) + +/*++ + +Routine Description: + + This function virtually unwinds the specified function by parsing the + .xdata record to determine where in the function the provided ControlPc + is, and then executing unwind codes that map to the function's prolog + or epilog behavior. + + If a context pointers record is specified (in the UnwindParams), then + the address where each nonvolatile register is restored from is recorded + in the appropriate element of the context pointers record. + +Arguments: + + ControlPcRva - Supplies the address where control left the specified + function, as an offset relative to the ImageBase. + + ImageBase - Supplies the base address of the image that contains the + function being unwound. + + FunctionEntry - Supplies the address of the function table entry for the + specified function. If appropriate, this should have already been + probed. + + ContextRecord - Supplies the address of a context record. + + EstablisherFrame - Supplies a pointer to a variable that receives the + the establisher frame pointer value. + + HandlerRoutine - Supplies an optional pointer to a variable that receives + the handler routine address. If control did not leave the specified + function in either the prolog or an epilog and a handler of the + proper type is associated with the function, then the address of the + language specific exception handler is returned. Otherwise, NULL is + returned. + + HandlerData - Supplies a pointer to a variable that receives a pointer + the language handler data. + + UnwindParams - Additional parameters shared with caller. + +Return Value: + + STATUS_SUCCESS if the unwind could be completed, a failure status otherwise. + Unwind can only fail when validation bounds are specified. + +--*/ + +{ + ULONG AccumulatedSaveNexts; + ULONG CurCode; + ULONG EpilogScopeCount; + PEXCEPTION_ROUTINE ExceptionHandler; + PVOID ExceptionHandlerData; + BOOLEAN FinalPcFromRa; + ULONG FunctionLength; + ULONG HeaderWord; + ULONG NextCode, NextCode1, NextCode2; + DWORD64 OffsetInFunction; + ULONG ScopeNum; + ULONG ScopeSize; + ULONG ScopeStart; + DWORD64 SkipWords; + NTSTATUS Status; + ULONG_PTR UnwindCodePtr; + ULONG_PTR UnwindCodesEndPtr; + ULONG_PTR UnwindDataPtr; + ULONG UnwindIndex; + ULONG UnwindWords; + + // + // Unless a special frame is encountered, assume that any unwinding + // will return us to the return address of a call and set the flag + // appropriately (it will be cleared again if the special cases apply). + // + + ContextRecord->ContextFlags |= CONTEXT_UNWOUND_TO_CALL; + + // + // By default, unwinding is done by popping to the RA, then copying + // that RA to the PC. However, some special opcodes require different + // behavior. + // + + FinalPcFromRa = TRUE; + + // + // Fetch the header word from the .xdata blob + // + + UnwindDataPtr = ImageBase + FunctionEntry->UnwindData; + HeaderWord = MEMORY_READ_DWORD(UnwindParams, UnwindDataPtr); + UnwindDataPtr += 4; + + // + // Verify the version before we do anything else + // + + if (((HeaderWord >> 18) & 3) != 0) { + assert(!"ShouldNotReachHere"); + return STATUS_UNWIND_UNSUPPORTED_VERSION; + } + + FunctionLength = HeaderWord & 0x3ffff; + OffsetInFunction = (ControlPcRva - FunctionEntry->BeginAddress) / 4; + + // + // Determine the number of epilog scope records and the maximum number + // of unwind codes. + // + + UnwindWords = (HeaderWord >> 27) & 31; + EpilogScopeCount = (HeaderWord >> 22) & 31; + if (EpilogScopeCount == 0 && UnwindWords == 0) { + EpilogScopeCount = MEMORY_READ_DWORD(UnwindParams, UnwindDataPtr); + UnwindDataPtr += 4; + UnwindWords = (EpilogScopeCount >> 16) & 0xff; + EpilogScopeCount &= 0xffff; + } + if ((HeaderWord & (1 << 21)) != 0) { + UnwindIndex = EpilogScopeCount; + EpilogScopeCount = 0; + } + + // + // If exception data is present, extract it now. + // + + ExceptionHandler = NULL; + ExceptionHandlerData = NULL; + if ((HeaderWord & (1 << 20)) != 0) { + ExceptionHandler = (PEXCEPTION_ROUTINE)(ImageBase + + MEMORY_READ_DWORD(UnwindParams, UnwindDataPtr + 4 * (EpilogScopeCount + UnwindWords))); + ExceptionHandlerData = (PVOID)(UnwindDataPtr + 4 * (EpilogScopeCount + UnwindWords + 1)); + } + + // + // Unless we are in a prolog/epilog, we execute the unwind codes + // that immediately follow the epilog scope list. + // + + UnwindCodePtr = UnwindDataPtr + 4 * EpilogScopeCount; + UnwindCodesEndPtr = UnwindCodePtr + 4 * UnwindWords; + SkipWords = 0; + + // + // If we're near the start of the function, and this function has a prolog, + // compute the size of the prolog from the unwind codes. If we're in the + // midst of it, we still execute starting at unwind code index 0, but we may + // need to skip some to account for partial execution of the prolog. + // + // N.B. As an optimization here, note that each byte of unwind codes can + // describe at most one 32-bit instruction. Thus, the largest prologue + // that could possibly be described by UnwindWords (which is 4 * the + // number of unwind code bytes) is 4 * UnwindWords words. If + // OffsetInFunction is larger than this value, it is guaranteed to be + // in the body of the function. + // + + if (OffsetInFunction < 4 * UnwindWords) { + ScopeSize = RtlpComputeScopeSize(UnwindCodePtr, UnwindCodesEndPtr, FALSE, UnwindParams); + + if (OffsetInFunction < ScopeSize) { + SkipWords = ScopeSize - OffsetInFunction; + ExceptionHandler = NULL; + ExceptionHandlerData = NULL; + goto ExecuteCodes; + } + } + + // + // We're not in the prolog, now check to see if we are in the epilog. + // In the simple case, the 'E' bit is set indicating there is a single + // epilog that lives at the end of the function. If we're near the end + // of the function, compute the actual size of the epilog from the + // unwind codes. If we're in the midst of it, adjust the unwind code + // pointer to the start of the codes and determine how many we need to skip. + // + // N.B. Similar to the prolog case above, the maximum number of halfwords + // that an epilog can cover is limited by UnwindWords. In the epilog + // case, however, the starting index within the unwind code table is + // non-zero, and so the maximum number of unwind codes that can pertain + // to an epilog is (UnwindWords * 4 - UnwindIndex), thus further + // constraining the bounds of the epilog. + // + + if ((HeaderWord & (1 << 21)) != 0) { + if (OffsetInFunction + (4 * UnwindWords - UnwindIndex) >= FunctionLength) { + ScopeSize = RtlpComputeScopeSize(UnwindCodePtr + UnwindIndex, UnwindCodesEndPtr, TRUE, UnwindParams); + ScopeStart = FunctionLength - ScopeSize; + + if (OffsetInFunction >= ScopeStart) { + UnwindCodePtr += UnwindIndex; + SkipWords = OffsetInFunction - ScopeStart; + ExceptionHandler = NULL; + ExceptionHandlerData = NULL; + } + } + } + + // + // In the multiple-epilog case, we scan forward to see if we are within + // shooting distance of any of the epilogs. If we are, we compute the + // actual size of the epilog from the unwind codes and proceed like the + // simple case above. + // + + else { + for (ScopeNum = 0; ScopeNum < EpilogScopeCount; ScopeNum++) { + HeaderWord = MEMORY_READ_DWORD(UnwindParams, UnwindDataPtr); + UnwindDataPtr += 4; + + // + // The scope records are stored in order. If we hit a record that + // starts after our current position, we must not be in an epilog. + // + + ScopeStart = HeaderWord & 0x3ffff; + if (OffsetInFunction < ScopeStart) { + break; + } + + UnwindIndex = HeaderWord >> 22; + if (OffsetInFunction < ScopeStart + (4 * UnwindWords - UnwindIndex)) { + ScopeSize = RtlpComputeScopeSize(UnwindCodePtr + UnwindIndex, UnwindCodesEndPtr, TRUE, UnwindParams); + + if (OffsetInFunction < ScopeStart + ScopeSize) { + + UnwindCodePtr += UnwindIndex; + SkipWords = OffsetInFunction - ScopeStart; + ExceptionHandler = NULL; + ExceptionHandlerData = NULL; + break; + } + } + } + } + +ExecuteCodes: + + // + // Skip over unwind codes until we account for the number of halfwords + // to skip. + // + + while (UnwindCodePtr < UnwindCodesEndPtr && SkipWords > 0) { + CurCode = MEMORY_READ_BYTE(UnwindParams, UnwindCodePtr); + if (OPCODE_IS_END(CurCode)) { + break; + } + UnwindCodePtr += UnwindCodeSizeTable[CurCode]; + SkipWords--; + } + + // + // Now execute codes until we hit the end. + // + + Status = STATUS_SUCCESS; + AccumulatedSaveNexts = 0; + while (UnwindCodePtr < UnwindCodesEndPtr && Status == STATUS_SUCCESS) { + + CurCode = MEMORY_READ_BYTE(UnwindParams, UnwindCodePtr); + UnwindCodePtr += 1; + + // + // alloc_s (000xxxxx): allocate small stack with size < 1024 (2^5 * 16) + // + + if (CurCode <= 0x1f) { + if (AccumulatedSaveNexts != 0) { + return STATUS_UNWIND_INVALID_SEQUENCE; + } + ContextRecord->Sp += 16 * (CurCode & 0x1f); + } + + // + // alloc_m (11000xxx|xxxxxxxx): allocate large stack with size < 32k (2^11 * 16). + // + + else if (CurCode <= 0xc7) { + if (AccumulatedSaveNexts != 0) { + return STATUS_UNWIND_INVALID_SEQUENCE; + } + ContextRecord->Sp += 16 * ((CurCode & 7) << 8); + ContextRecord->Sp += 16 * MEMORY_READ_BYTE(UnwindParams, UnwindCodePtr); + UnwindCodePtr++; + } + + // + // save_reg (11010000|000xxxxx|zzzzzzzz): save reg r(1+#X) at [sp+#Z*8], offset <= 2047 + // + + else if (CurCode == 0xd0) { + if (AccumulatedSaveNexts != 0) { + return STATUS_UNWIND_INVALID_SEQUENCE; + } + NextCode = MEMORY_READ_BYTE(UnwindParams, UnwindCodePtr); + UnwindCodePtr++; + NextCode1 = (uint8_t)MEMORY_READ_BYTE(UnwindParams, UnwindCodePtr); + UnwindCodePtr++; + Status = RtlpUnwindRestoreRegisterRange( + ContextRecord, + 8 * NextCode1, + 1 + NextCode, + 1, + UnwindParams); + } + + // + // save_freg (11011100|0xxxzzzz|zzzzzzzz): save reg f(24+#X) at [sp+#Z*8], offset <= 32767 + // + + else if (CurCode == 0xdc) { + if (AccumulatedSaveNexts != 0) { + return STATUS_UNWIND_INVALID_SEQUENCE; + } + NextCode = MEMORY_READ_BYTE(UnwindParams, UnwindCodePtr); + UnwindCodePtr++; + NextCode1 = MEMORY_READ_BYTE(UnwindParams, UnwindCodePtr); + UnwindCodePtr++; + Status = RtlpUnwindRestoreFpRegisterRange( + ContextRecord, + 8 * (((NextCode & 0xf) << 8) + NextCode1), + 24 + (NextCode >> 4), + 1, + UnwindParams); + } + + // + // alloc_l (11100000|xxxxxxxx|xxxxxxxx|xxxxxxxx): allocate large stack with size < 256M + // + + else if (CurCode == 0xe0) { + if (AccumulatedSaveNexts != 0) { + return STATUS_UNWIND_INVALID_SEQUENCE; + } + ContextRecord->Sp += 16 * (MEMORY_READ_BYTE(UnwindParams, UnwindCodePtr) << 16); + UnwindCodePtr++; + ContextRecord->Sp += 16 * (MEMORY_READ_BYTE(UnwindParams, UnwindCodePtr) << 8); + UnwindCodePtr++; + ContextRecord->Sp += 16 * MEMORY_READ_BYTE(UnwindParams, UnwindCodePtr); + UnwindCodePtr++; + } + + // + // set_fp (11100001): set up fp: with: ori fp,sp,0 + // + + else if (CurCode == 0xe1) { + if (AccumulatedSaveNexts != 0) { + return STATUS_UNWIND_INVALID_SEQUENCE; + } + ContextRecord->Sp = ContextRecord->Fp; + } + + // + // add_fp (11100010|000xxxxx|xxxxxxxx): set up fp with: addi.d fp,sp,#x*8 + // + + else if (CurCode == 0xe2) { + if (AccumulatedSaveNexts != 0) { + return STATUS_UNWIND_INVALID_SEQUENCE; + } + NextCode = MEMORY_READ_BYTE(UnwindParams, UnwindCodePtr); + UnwindCodePtr++; + NextCode1 = MEMORY_READ_BYTE(UnwindParams, UnwindCodePtr); + UnwindCodePtr++; + ContextRecord->Sp = ContextRecord->Fp - 8 * ((NextCode << 8) | NextCode1); + } + + // + // nop (11100011): no unwind operation is required + // + + else if (CurCode == 0xe3) { + if (AccumulatedSaveNexts != 0) { + return STATUS_UNWIND_INVALID_SEQUENCE; + } + } + + // + // end (11100100): end of unwind code + // + + else if (CurCode == 0xe4) { + if (AccumulatedSaveNexts != 0) { + return STATUS_UNWIND_INVALID_SEQUENCE; + } + goto finished; + } + + // + // end_c (11100101): end of unwind code in current chained scope + // + + else if (CurCode == 0xe5) { + if (AccumulatedSaveNexts != 0) { + return STATUS_UNWIND_INVALID_SEQUENCE; + } + goto finished; + } + + // + // custom_0 (111010xx): restore custom structure + // + + else if (CurCode >= 0xe8 && CurCode <= 0xeb) { + if (AccumulatedSaveNexts != 0) { + return STATUS_UNWIND_INVALID_SEQUENCE; + } + Status = RtlpUnwindCustom(ContextRecord, (BYTE) CurCode, UnwindParams); + FinalPcFromRa = FALSE; + } + + // + // Anything else is invalid + // + + else { + return STATUS_UNWIND_INVALID_SEQUENCE; + } + } + + // + // If we succeeded, post-process the results a bit + // +finished: + if (Status == STATUS_SUCCESS) { + + // + // Since we always POP to the RA, recover the final PC from there, unless + // it was overwritten due to a special case custom unwinding operation. + // Also set the establisher frame equal to the final stack pointer. + // + + if (FinalPcFromRa) { + ContextRecord->Pc = ContextRecord->Ra; + } + + *EstablisherFrame = ContextRecord->Sp; + + if (ARGUMENT_PRESENT(HandlerRoutine)) { + *HandlerRoutine = ExceptionHandler; + } + *HandlerData = ExceptionHandlerData; + } + + return Status; +} + +NTSTATUS +RtlpUnwindFunctionCompact( + _In_ DWORD64 ControlPcRva, + _In_ PT_RUNTIME_FUNCTION FunctionEntry, + __inout T_CONTEXT *ContextRecord, + _Out_ PDWORD64 EstablisherFrame, + __deref_opt_out_opt PEXCEPTION_ROUTINE *HandlerRoutine, + _Out_ PVOID *HandlerData, + _In_ PRISCV64_UNWIND_PARAMS UnwindParams + ) + +/*++ + +Routine Description: + + This function virtually unwinds the specified function by parsing the + compact .pdata record to determine where in the function the provided + ControlPc is, and then executing a standard, well-defined set of + operations. + + If a context pointers record is specified (in the UnwindParams), then + the address where each nonvolatile register is restored from is recorded + in the appropriate element of the context pointers record. + +Arguments: + + ControlPcRva - Supplies the address where control left the specified + function, as an offset relative to the ImageBase. + + FunctionEntry - Supplies the address of the function table entry for the + specified function. If appropriate, this should have already been + probed. + + ContextRecord - Supplies the address of a context record. + + EstablisherFrame - Supplies a pointer to a variable that receives the + the establisher frame pointer value. + + HandlerRoutine - Supplies an optional pointer to a variable that receives + the handler routine address. If control did not leave the specified + function in either the prolog or an epilog and a handler of the + proper type is associated with the function, then the address of the + language specific exception handler is returned. Otherwise, NULL is + returned. + + HandlerData - Supplies a pointer to a variable that receives a pointer + the language handler data. + + UnwindParams - Additional parameters shared with caller. + +Return Value: + + STATUS_SUCCESS if the unwind could be completed, a failure status otherwise. + Unwind can only fail when validation bounds are specified. + +--*/ + +{ + ULONG Count; + ULONG Cr; + ULONG CurrentOffset; + ULONG EpilogLength; + ULONG Flag; + ULONG FloatSize; + ULONG FrameSize; + ULONG FRegOpcodes; + ULONG FunctionLength; + ULONG HBit; + ULONG HOpcodes; + ULONG IRegOpcodes; + ULONG IntSize; + ULONG LocalSize; + DWORD64 OffsetInFunction; + DWORD64 OffsetInScope; + ULONG PrologLength; + ULONG RegF; + ULONG RegI; + ULONG RegSize; + ULONG ScopeStart; + ULONG StackAdjustOpcodes; + NTSTATUS Status; + ULONG UnwindData; + + UnwindData = FunctionEntry->UnwindData; + Status = STATUS_SUCCESS; + + // + // Compact records always describe an unwind to a call. + // + + ContextRecord->ContextFlags |= CONTEXT_UNWOUND_TO_CALL; + + // + // Extract the basic information about how to do a full unwind. + // + + Flag = UnwindData & 3; + FunctionLength = (UnwindData >> 2) & 0x7ff; + RegF = (UnwindData >> 13) & 7; + RegI = (UnwindData >> 16) & 0xf; + HBit = (UnwindData >> 20) & 1; + Cr = (UnwindData >> 21) & 3; + FrameSize = (UnwindData >> 23) & 0x1ff; + + assert(!"---------------RISCV64 ShouldNotReachHere"); + if (Flag == 3) { + return STATUS_UNWIND_INVALID_SEQUENCE; + } + if (Cr == 2) { + return STATUS_UNWIND_INVALID_SEQUENCE; + } + + // + // Determine the size of the locals + // + + IntSize = RegI * 8; + if (Cr == 1) { + IntSize += 8; + } + FloatSize = (RegF == 0) ? 0 : (RegF + 1) * 8; + RegSize = (IntSize + FloatSize + 8*8 * HBit + 0xf) & ~0xf; + if (RegSize > 16 * FrameSize) { + return STATUS_UNWIND_INVALID_SEQUENCE; + } + LocalSize = 16 * FrameSize - RegSize; + + // + // If we're near the start of the function (within 17 words), + // see if we are within the prolog. + // + // N.B. If the low 2 bits of the UnwindData are 2, then we have + // no prolog. + // + + OffsetInFunction = (ControlPcRva - FunctionEntry->BeginAddress) / 4; + OffsetInScope = 0; + if (OffsetInFunction < 17 && Flag != 2) { + + // + // Compute sizes for each opcode in the prolog. + // + + IRegOpcodes = (IntSize + 8) / 16; + FRegOpcodes = (FloatSize + 8) / 16; + HOpcodes = 4 * HBit; + StackAdjustOpcodes = (Cr == 3) ? 1 : 0; + if (Cr != 3 || LocalSize > 512) { + StackAdjustOpcodes += (LocalSize > 4088) ? 2 : (LocalSize > 0) ? 1 : 0; + } + + // + // Compute the total prolog length and determine if we are within + // its scope. + // + // N.B. We must execute prolog operations backwards to unwind, so + // our final scope offset in this case is the distance from the end. + // + + PrologLength = IRegOpcodes + FRegOpcodes + HOpcodes + StackAdjustOpcodes; + + if (OffsetInFunction < PrologLength) { + OffsetInScope = PrologLength - OffsetInFunction; + } + } + + // + // If we're near the end of the function (within 15 words), see if + // we are within the epilog. + // + // N.B. If the low 2 bits of the UnwindData are 2, then we have + // no epilog. + // + + if (OffsetInScope == 0 && OffsetInFunction + 15 >= FunctionLength && Flag != 2) { + + // + // Compute sizes for each opcode in the epilog. + // + + IRegOpcodes = (IntSize + 8) / 16; + FRegOpcodes = (FloatSize + 8) / 16; + HOpcodes = HBit; + StackAdjustOpcodes = (Cr == 3) ? 1 : 0; + if (Cr != 3 || LocalSize > 512) { + StackAdjustOpcodes += (LocalSize > 4088) ? 2 : (LocalSize > 0) ? 1 : 0; + } + + // + // Compute the total epilog length and determine if we are within + // its scope. + // + + EpilogLength = IRegOpcodes + FRegOpcodes + HOpcodes + StackAdjustOpcodes + 1; + + ScopeStart = FunctionLength - EpilogLength; + if (OffsetInFunction > ScopeStart) { + OffsetInScope = OffsetInFunction - ScopeStart; + } + } + + // + // Process operations backwards, in the order: stack/frame deallocation, + // VFP register popping, integer register popping, parameter home + // area recovery. + // + // First case is simple: we process everything with no regard for + // the current offset within the scope. + // + + Status = STATUS_SUCCESS; + if (OffsetInScope == 0) { + + if (Cr == 3) { + Status = RtlpUnwindRestoreRegisterRange(ContextRecord, 0, 22, 1, UnwindParams);///fp + assert(Status == STATUS_SUCCESS); + Status = RtlpUnwindRestoreRegisterRange(ContextRecord, 0, 1, 1, UnwindParams);//ra + } + ContextRecord->Sp += LocalSize; + + if (RegF != 0 && Status == STATUS_SUCCESS) { + Status = RtlpUnwindRestoreFpRegisterRange(ContextRecord, IntSize, 24, RegF + 1, UnwindParams);//fs0 + } + + if (Cr == 1 && Status == STATUS_SUCCESS) { + Status = RtlpUnwindRestoreRegisterRange(ContextRecord, IntSize - 8, 1, 1, UnwindParams);//ra + } + if (RegI > 0 && Status == STATUS_SUCCESS) { + Status = RtlpUnwindRestoreRegisterRange(ContextRecord, 0, 23, RegI, UnwindParams);//s0 + } + ContextRecord->Sp += RegSize; + } + + // + // Second case is more complex: we must step along each operation + // to ensure it should be executed. + // + + else { + + CurrentOffset = 0; + if (Cr == 3) { + if (LocalSize <= 512) { + if (CurrentOffset++ >= OffsetInScope) { + Status = RtlpUnwindRestoreRegisterRange(ContextRecord, -(LONG)LocalSize, 22, 1, UnwindParams); + Status = RtlpUnwindRestoreRegisterRange(ContextRecord, -(LONG)LocalSize, 1, 1, UnwindParams); + } + LocalSize = 0; + } + } + while (LocalSize != 0) { + Count = (LocalSize + 4087) % 4088 + 1; + if (CurrentOffset++ >= OffsetInScope) { + ContextRecord->Sp += Count; + } + LocalSize -= Count; + } + + if (HBit != 0) { + CurrentOffset += 4; + } + + if (RegF != 0 && Status == STATUS_SUCCESS) { + RegF++; + while (RegF != 0) { + Count = 2 - (RegF & 1); + RegF -= Count; + if (CurrentOffset++ >= OffsetInScope) { + Status = RtlpUnwindRestoreFpRegisterRange( + ContextRecord, + (RegF == 0 && RegI == 0) ? (-(LONG)RegSize) : (IntSize + 8 * RegF), + 24 + RegF, + Count, + UnwindParams); + } + } + } + + if (Cr == 1 && Status == STATUS_SUCCESS) { + if (RegI % 2 == 0) { + if (CurrentOffset++ >= OffsetInScope) { + Status = RtlpUnwindRestoreRegisterRange(ContextRecord, IntSize - 8, 31, 1, UnwindParams);//s8 ? + } + } else { + if (CurrentOffset++ >= OffsetInScope) { + RegI--; + Status = RtlpUnwindRestoreRegisterRange(ContextRecord, IntSize - 8, 2, 1, UnwindParams);//tp ? + if (Status == STATUS_SUCCESS) { + Status = RtlpUnwindRestoreRegisterRange(ContextRecord, IntSize - 16, 23 + RegI, 1, UnwindParams); + } + } + } + } + + while (RegI != 0 && Status == STATUS_SUCCESS) { + Count = 2 - (RegI & 1); + RegI -= Count; + if (CurrentOffset++ >= OffsetInScope) { + Status = RtlpUnwindRestoreRegisterRange( + ContextRecord, + (RegI == 0) ? (-(LONG)RegSize) : (8 * RegI), + 23 + RegI, + Count, + UnwindParams); + } + } + } + + // + // If we succeeded, post-process the results a bit + // + + if (Status == STATUS_SUCCESS) { + + ContextRecord->Pc = ContextRecord->Ra; + *EstablisherFrame = ContextRecord->Sp; + + if (ARGUMENT_PRESENT(HandlerRoutine)) { + *HandlerRoutine = NULL; + } + *HandlerData = NULL; + } + + return Status; +} + +BOOL OOPStackUnwinderRISCV64::Unwind(T_CONTEXT * pContext) +{ + DWORD64 ImageBase = 0; + HRESULT hr = GetModuleBase(pContext->Pc, &ImageBase); + if (hr != S_OK) + return FALSE; + + PEXCEPTION_ROUTINE DummyHandlerRoutine; + PVOID DummyHandlerData; + DWORD64 DummyEstablisherFrame; + + DWORD64 startingPc = pContext->Pc; + DWORD64 startingSp = pContext->Sp; + + T_RUNTIME_FUNCTION Rfe; + if (FAILED(GetFunctionEntry(pContext->Pc, &Rfe, sizeof(Rfe)))) + return FALSE; + + if ((Rfe.UnwindData & 3) != 0) + { + hr = RtlpUnwindFunctionCompact(pContext->Pc - ImageBase, + &Rfe, + pContext, + &DummyEstablisherFrame, + &DummyHandlerRoutine, + &DummyHandlerData, + NULL); + + } + else + { + hr = RtlpUnwindFunctionFull(pContext->Pc - ImageBase, + ImageBase, + &Rfe, + pContext, + &DummyEstablisherFrame, + &DummyHandlerRoutine, + &DummyHandlerData, + NULL); + } + + // PC == 0 means unwinding is finished. + // Same if no forward progress is made + if (pContext->Pc == 0 || (startingPc == pContext->Pc && startingSp == pContext->Sp)) + return FALSE; + + return TRUE; +} + +BOOL DacUnwindStackFrame(T_CONTEXT *pContext, T_KNONVOLATILE_CONTEXT_POINTERS* pContextPointers) +{ + OOPStackUnwinderRISCV64 unwinder; + BOOL res = unwinder.Unwind(pContext); + + if (res && pContextPointers) + { + for (int i = 0; i < 11; i++) + { + *(&pContextPointers->S1 + i) = &pContext->S1 + i; + } + pContextPointers->Fp = &pContext->Fp; + pContextPointers->Ra = &pContext->Ra; + } + + return res; +} + +#if defined(HOST_UNIX) +PEXCEPTION_ROUTINE +RtlVirtualUnwind( + IN ULONG HandlerType, + IN ULONG64 ImageBase, + IN ULONG64 ControlPc, + IN PT_RUNTIME_FUNCTION FunctionEntry, + IN OUT PCONTEXT ContextRecord, + OUT PVOID *HandlerData, + OUT PULONG64 EstablisherFrame, + IN OUT PT_KNONVOLATILE_CONTEXT_POINTERS ContextPointers OPTIONAL + ) +{ + PEXCEPTION_ROUTINE handlerRoutine; + HRESULT hr; + + DWORD64 startingPc = ControlPc; + DWORD64 startingSp = ContextRecord->Sp; + + T_RUNTIME_FUNCTION rfe; + + rfe.BeginAddress = FunctionEntry->BeginAddress; + rfe.UnwindData = FunctionEntry->UnwindData; + + RISCV64_UNWIND_PARAMS unwindParams; + unwindParams.ContextPointers = ContextPointers; + + if ((rfe.UnwindData & 3) != 0) + { + hr = RtlpUnwindFunctionCompact(ControlPc - ImageBase, + &rfe, + ContextRecord, + EstablisherFrame, + &handlerRoutine, + HandlerData, + &unwindParams); + + } + else + { + hr = RtlpUnwindFunctionFull(ControlPc - ImageBase, + ImageBase, + &rfe, + ContextRecord, + EstablisherFrame, + &handlerRoutine, + HandlerData, + &unwindParams); + } + + return handlerRoutine; +} +#endif diff --git a/src/coreclr/unwinder/riscv64/unwinder.h b/src/coreclr/unwinder/riscv64/unwinder.h index efcd109cceb5dc..0b12326b2cbb09 100644 --- a/src/coreclr/unwinder/riscv64/unwinder.h +++ b/src/coreclr/unwinder/riscv64/unwinder.h @@ -8,6 +8,44 @@ #include "baseunwinder.h" -#error "TODO-RISCV64: missing implementation" +//--------------------------------------------------------------------------------------- +// +// See the comment for the base class code:OOPStackUnwinder. +// + +class OOPStackUnwinderRISCV64 : public OOPStackUnwinder +{ +public: + // Unwind the given CONTEXT to the caller CONTEXT. The CONTEXT will be overwritten. + BOOL Unwind(T_CONTEXT * pContext); + + // + // Everything below comes from dbghelp.dll. + // + +protected: + HRESULT UnwindPrologue(_In_ DWORD64 ImageBase, + _In_ DWORD64 ControlPc, + _In_ DWORD64 FrameBase, + _In_ _PIMAGE_RUNTIME_FUNCTION_ENTRY FunctionEntry, + __inout PT_CONTEXT ContextRecord); + + HRESULT VirtualUnwind(_In_ DWORD64 ImageBase, + _In_ DWORD64 ControlPc, + _In_ _PIMAGE_RUNTIME_FUNCTION_ENTRY FunctionEntry, + __inout PT_CONTEXT ContextRecord, + _Out_ PDWORD64 EstablisherFrame); + + DWORD64 LookupPrimaryUnwindInfo + (_In_ _PIMAGE_RUNTIME_FUNCTION_ENTRY FunctionEntry, + _In_ DWORD64 ImageBase, + _Out_ _PIMAGE_RUNTIME_FUNCTION_ENTRY PrimaryEntry); + + _PIMAGE_RUNTIME_FUNCTION_ENTRY SameFunction + (_In_ _PIMAGE_RUNTIME_FUNCTION_ENTRY FunctionEntry, + _In_ DWORD64 ImageBase, + _In_ DWORD64 ControlPc, + _Out_ _PIMAGE_RUNTIME_FUNCTION_ENTRY FunctionReturnBuffer); +}; #endif // __unwinder_riscv64__ diff --git a/src/coreclr/utilcode/util.cpp b/src/coreclr/utilcode/util.cpp index 0901e06d1f3028..76f07e451c47c2 100644 --- a/src/coreclr/utilcode/util.cpp +++ b/src/coreclr/utilcode/util.cpp @@ -561,7 +561,7 @@ BYTE * ClrVirtualAllocWithinRange(const BYTE *pMinAddr, /*static*/ CPU_Group_Info *CPUGroupInfo::m_CPUGroupInfoArray = NULL; /*static*/ LONG CPUGroupInfo::m_initialization = 0; -#if !defined(FEATURE_NATIVEAOT) && (defined(TARGET_AMD64) || defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64)) +#if !defined(FEATURE_NATIVEAOT) && (defined(TARGET_AMD64) || defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64)) // Calculate greatest common divisor DWORD GCD(DWORD u, DWORD v) { @@ -591,7 +591,7 @@ DWORD LCM(DWORD u, DWORD v) } CONTRACTL_END; -#if !defined(FEATURE_NATIVEAOT) && (defined(TARGET_AMD64) || defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64)) +#if !defined(FEATURE_NATIVEAOT) && (defined(TARGET_AMD64) || defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64)) BYTE *bBuffer = NULL; SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX *pSLPIEx = NULL; SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX *pRecord = NULL; @@ -672,7 +672,7 @@ DWORD LCM(DWORD u, DWORD v) } CONTRACTL_END; -#if !defined(FEATURE_NATIVEAOT) && (defined(TARGET_AMD64) || defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64)) +#if !defined(FEATURE_NATIVEAOT) && (defined(TARGET_AMD64) || defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64)) USHORT groupCount = 0; // On Windows 11+ and Windows Server 2022+, a process is no longer restricted to a single processor group by default. @@ -758,7 +758,7 @@ DWORD LCM(DWORD u, DWORD v) { LIMITED_METHOD_CONTRACT; -#if !defined(FEATURE_NATIVEAOT) && (defined(TARGET_AMD64) || defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64)) +#if !defined(FEATURE_NATIVEAOT) && (defined(TARGET_AMD64) || defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64)) WORD bTemp = 0; WORD bDiff = processor_number - bTemp; @@ -789,7 +789,7 @@ DWORD LCM(DWORD u, DWORD v) } CONTRACTL_END; -#if !defined(FEATURE_NATIVEAOT) && (defined(TARGET_AMD64) || defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64)) +#if !defined(FEATURE_NATIVEAOT) && (defined(TARGET_AMD64) || defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64)) _ASSERTE(m_enableGCCPUGroups && m_threadUseAllCpuGroups); PROCESSOR_NUMBER proc_no; @@ -838,7 +838,7 @@ DWORD LCM(DWORD u, DWORD v) } CONTRACTL_END; -#if (defined(TARGET_AMD64) || defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64)) +#if (defined(TARGET_AMD64) || defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64)) WORD i, minGroup = 0; DWORD minWeight = 0; @@ -880,7 +880,7 @@ DWORD LCM(DWORD u, DWORD v) /*static*/ void CPUGroupInfo::ClearCPUGroupAffinity(GROUP_AFFINITY *gf) { LIMITED_METHOD_CONTRACT; -#if (defined(TARGET_AMD64) || defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64)) +#if (defined(TARGET_AMD64) || defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64)) _ASSERTE(m_enableGCCPUGroups && m_threadUseAllCpuGroups && m_threadAssignCpuGroups); WORD group = gf->Group; diff --git a/src/coreclr/vm/CMakeLists.txt b/src/coreclr/vm/CMakeLists.txt index 7d7826b5a0d70a..f09f43c432d664 100644 --- a/src/coreclr/vm/CMakeLists.txt +++ b/src/coreclr/vm/CMakeLists.txt @@ -758,6 +758,14 @@ else(CLR_CMAKE_TARGET_WIN32) ${ARCH_SOURCES_DIR}/pinvokestubs.S ${ARCH_SOURCES_DIR}/thunktemplates.S ) + elseif(CLR_CMAKE_TARGET_ARCH_RISCV64) + set(VM_SOURCES_WKS_ARCH_ASM + ${ARCH_SOURCES_DIR}/asmhelpers.S + ${ARCH_SOURCES_DIR}/calldescrworkerriscv64.S + ${ARCH_SOURCES_DIR}/crthelpers.S + ${ARCH_SOURCES_DIR}/pinvokestubs.S + ${ARCH_SOURCES_DIR}/thunktemplates.S + ) endif() endif(CLR_CMAKE_TARGET_WIN32) @@ -860,7 +868,22 @@ elseif(CLR_CMAKE_TARGET_ARCH_ARM64) ${ARCH_SOURCES_DIR}/singlestepper.cpp ) endif(CLR_CMAKE_HOST_UNIX) -elseif(CLR_CMAKE_TARGET_ARCH_LOONGARCH64) +elseif(clr_cmake_target_arch_loongarch64) + set(vm_sources_dac_and_wks_arch + ${arch_sources_dir}/stubs.cpp + exceptionhandling.cpp + ) + + set(vm_headers_dac_and_wks_arch + ${arch_sources_dir}/virtualcallstubcpu.hpp + exceptionhandling.h + ) + + set(vm_sources_wks_arch + ${arch_sources_dir}/profiler.cpp + gcinfodecoder.cpp + ) +elseif(CLR_CMAKE_TARGET_ARCH_RISCV64) set(VM_SOURCES_DAC_AND_WKS_ARCH ${ARCH_SOURCES_DIR}/stubs.cpp exceptionhandling.cpp diff --git a/src/coreclr/vm/arm64/cgencpu.h b/src/coreclr/vm/arm64/cgencpu.h index ea29ec2bdce028..39c3184aaea07f 100644 --- a/src/coreclr/vm/arm64/cgencpu.h +++ b/src/coreclr/vm/arm64/cgencpu.h @@ -2,7 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // - #ifndef TARGET_ARM64 #error Should only include "cGenCpu.h" for ARM64 builds #endif diff --git a/src/coreclr/vm/callcounting.h b/src/coreclr/vm/callcounting.h index 3d25e1c2826267..f47bfc05c16757 100644 --- a/src/coreclr/vm/callcounting.h +++ b/src/coreclr/vm/callcounting.h @@ -99,6 +99,8 @@ class CallCountingStub static const int CodeSize = 32; #elif defined(TARGET_LOONGARCH64) static const int CodeSize = 40; +#elif defined(TARGET_RISCV64) + static const int CodeSize = 40; // TODO RISCV64 #endif private: diff --git a/src/coreclr/vm/callingconvention.h b/src/coreclr/vm/callingconvention.h index 364c850fca49b3..6841f3eee9ca63 100644 --- a/src/coreclr/vm/callingconvention.h +++ b/src/coreclr/vm/callingconvention.h @@ -41,7 +41,7 @@ struct ArgLocDesc int m_byteStackIndex; // Stack offset in bytes (or -1) int m_byteStackSize; // Stack size in bytes -#if defined(TARGET_LOONGARCH64) +#if defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64) int m_structFields; // Struct field info when using Float-register except two-doubles case. #endif @@ -96,7 +96,7 @@ struct ArgLocDesc #if defined(TARGET_ARM64) m_hfaFieldSize = 0; #endif // defined(TARGET_ARM64) -#if defined(TARGET_LOONGARCH64) +#if defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64) m_structFields = STRUCT_NO_FLOAT_FIELD; #endif #if defined(UNIX_AMD64_ABI) @@ -164,6 +164,29 @@ struct TransitionBlock }; //TADDR padding; // Keep size of TransitionBlock as multiple of 16-byte. Simplifies code in PROLOG_WITH_TRANSITION_BLOCK ArgumentRegisters m_argumentRegisters; +#elif defined(TARGET_RISCV64) + union { + CalleeSavedRegisters m_calleeSavedRegisters; + struct { + INT64 s0; // frame pointer + TADDR m_ReturnAddress; + INT64 s1; + INT64 s2; + INT64 s3; + INT64 s4; + INT64 s5; + INT64 s6; + INT64 s7; + INT64 s8; + INT64 s9; + INT64 s10; + INT64 s11; + INT64 tp; + INT64 gp; + }; + }; + //TADDR padding; // Keep size of TransitionBlock as multiple of 16-byte. Simplifies code in PROLOG_WITH_TRANSITION_BLOCK + ArgumentRegisters m_argumentRegisters; #else PORTABILITY_ASSERT("TransitionBlock"); #endif @@ -505,6 +528,8 @@ class ArgIteratorTemplate : public ARGITERATOR_BASE #elif defined(TARGET_LOONGARCH64) // Composites greater than 16 bytes are passed by reference return (size > ENREGISTERED_PARAMTYPE_MAXSIZE); +#elif defined(TARGET_RISCV64) + return (size > ENREGISTERED_PARAMTYPE_MAXSIZE); #else PORTABILITY_ASSERT("ArgIteratorTemplate::IsArgPassedByRef"); return FALSE; @@ -566,6 +591,13 @@ class ArgIteratorTemplate : public ARGITERATOR_BASE return (m_argSize > ENREGISTERED_PARAMTYPE_MAXSIZE); } return FALSE; +#elif defined(TARGET_RISCV64) + if (m_argType == ELEMENT_TYPE_VALUETYPE) + { + _ASSERTE(!m_argTypeHandle.IsNull()); + return ((m_argSize > ENREGISTERED_PARAMTYPE_MAXSIZE) && (!m_argTypeHandle.IsHFA() || this->IsVarArg())); + } + return FALSE; #else PORTABILITY_ASSERT("ArgIteratorTemplate::IsArgPassedByRef"); return FALSE; @@ -627,7 +659,7 @@ class ArgIteratorTemplate : public ARGITERATOR_BASE ArgLocDesc* GetArgLocDescForStructInRegs() { -#if defined(UNIX_AMD64_ABI) || defined (TARGET_ARM64) || defined(TARGET_LOONGARCH64) +#if defined(UNIX_AMD64_ABI) || defined (TARGET_ARM64) || defined(TARGET_LOONGARCH64) || defined (TARGET_RISCV64) return m_hasArgLocDescForStructInRegs ? &m_argLocDescForStructInRegs : NULL; #else return NULL; @@ -876,6 +908,56 @@ class ArgIteratorTemplate : public ARGITERATOR_BASE #endif // TARGET_LOONGARCH64 +#ifdef TARGET_RISCV64 + // Get layout information for the argument that the ArgIterator is currently visiting. + void GetArgLoc(int argOffset, ArgLocDesc *pLoc) + { + LIMITED_METHOD_CONTRACT; + + pLoc->Init(); + + if (m_hasArgLocDescForStructInRegs) + { + *pLoc = m_argLocDescForStructInRegs; + return; + } + + if (TransitionBlock::IsFloatArgumentRegisterOffset(argOffset)) + { + // TODO-RISCV64: support SIMD. + // Dividing by 8 as size of each register in FloatArgumentRegisters is 8 bytes. + pLoc->m_idxFloatReg = (argOffset - TransitionBlock::GetOffsetOfFloatArgumentRegisters()) / 8; + + assert(!m_argTypeHandle.IsHFA()); + + pLoc->m_cFloatReg = 1; + + return; + } + + int cSlots = (GetArgSize() + 7)/ 8; + + // Composites greater than 16bytes are passed by reference + if (GetArgType() == ELEMENT_TYPE_VALUETYPE && GetArgSize() > ENREGISTERED_PARAMTYPE_MAXSIZE) + { + cSlots = 1; + } + + if (!TransitionBlock::IsStackArgumentOffset(argOffset)) + { + // At least one used integer register passed. + pLoc->m_idxGenReg = TransitionBlock::GetArgumentIndexFromOffset(argOffset); + pLoc->m_cGenReg = cSlots; + } + else + { + pLoc->m_byteStackIndex = TransitionBlock::GetStackArgumentByteIndexFromOffset(argOffset); + pLoc->m_byteStackSize = cSlots << 3; + } + + return; + } +#endif // TARGET_RISCV64 protected: DWORD m_dwFlags; // Cached flags int m_nSizeOfArgStack; // Cached value of SizeOfArgStack @@ -886,10 +968,10 @@ class ArgIteratorTemplate : public ARGITERATOR_BASE CorElementType m_argType; int m_argSize; TypeHandle m_argTypeHandle; -#if (defined(TARGET_AMD64) && defined(UNIX_AMD64_ABI)) || defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64) +#if (defined(TARGET_AMD64) && defined(UNIX_AMD64_ABI)) || defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64) ArgLocDesc m_argLocDescForStructInRegs; bool m_hasArgLocDescForStructInRegs; -#endif // (TARGET_AMD64 && UNIX_AMD64_ABI) || TARGET_ARM64 || TARGET_LOONGARCH64 +#endif // (TARGET_AMD64 && UNIX_AMD64_ABI) || TARGET_ARM64 || TARGET_LOONGARCH64 || TARGET_RISCV64 int m_ofsStack; // Current position of the stack iterator, in bytes @@ -923,6 +1005,12 @@ class ArgIteratorTemplate : public ARGITERATOR_BASE int m_idxFPReg; // Next FP register to be assigned a value #endif +#ifdef TARGET_RISCV64 + int m_idxGenReg; // Next general register to be assigned a value + int m_idxStack; // Next stack slot to be assigned a value + int m_idxFPReg; // Next FP register to be assigned a value +#endif + enum { ITERATION_STARTED = 0x0001, // Started iterating over arguments SIZE_OF_ARG_STACK_COMPUTED = 0x0002, @@ -1170,6 +1258,10 @@ int ArgIteratorTemplate::GetNextOffset() m_idxGenReg = numRegistersUsed; m_ofsStack = 0; m_idxFPReg = 0; +#elif defined(TARGET_RISCV64) + m_idxGenReg = numRegistersUsed; + m_ofsStack = 0; + m_idxFPReg = 0; #else PORTABILITY_ASSERT("ArgIteratorTemplate::GetNextOffset"); #endif @@ -1199,7 +1291,7 @@ int ArgIteratorTemplate::GetNextOffset() m_argSize = argSize; m_argTypeHandle = thValueType; -#if defined(UNIX_AMD64_ABI) || defined (TARGET_ARM64) || defined (TARGET_LOONGARCH64) +#if defined(UNIX_AMD64_ABI) || defined (TARGET_ARM64) || defined (TARGET_LOONGARCH64) || defined (TARGET_RISCV64) m_hasArgLocDescForStructInRegs = false; #endif @@ -1750,6 +1842,130 @@ int ArgIteratorTemplate::GetNextOffset() int argOfs = TransitionBlock::GetOffsetOfArgs() + m_ofsStack; m_ofsStack += ALIGN_UP(cbArg, TARGET_POINTER_SIZE); + return argOfs; +#elif defined(TARGET_RISCV64) + + int cFPRegs = 0; + int flags = 0; + + switch (argType) + { + + case ELEMENT_TYPE_R4: + // 32-bit floating point argument. + cFPRegs = 1; + break; + + case ELEMENT_TYPE_R8: + // 64-bit floating point argument. + cFPRegs = 1; + break; + + case ELEMENT_TYPE_VALUETYPE: + { + // Handle struct which containing floats or doubles that can be passed + // in FP registers if possible. + + // Composite greater than 16bytes should be passed by reference + if (argSize > ENREGISTERED_PARAMTYPE_MAXSIZE) + { + argSize = sizeof(TADDR); + } + else + { + MethodTable* pMethodTable = nullptr; + + if (!thValueType.IsTypeDesc()) + pMethodTable = thValueType.AsMethodTable(); + else + { + _ASSERTE(thValueType.IsNativeValueType()); + pMethodTable = thValueType.AsNativeValueType(); + } + _ASSERTE(pMethodTable != nullptr); + flags = MethodTable::GetRiscv64PassStructInRegisterFlags((CORINFO_CLASS_HANDLE)pMethodTable); + if (flags & STRUCT_HAS_FLOAT_FIELDS_MASK) + { + cFPRegs = (flags & STRUCT_FLOAT_FIELD_ONLY_TWO) ? 2 : 1; + } + } + + break; + } + + default: + break; + } + + const bool isValueType = (argType == ELEMENT_TYPE_VALUETYPE); + const bool isFloatHfa = thValueType.IsFloatHfa(); + const int cbArg = StackElemSize(argSize, isValueType, isFloatHfa); + + if (cFPRegs > 0 && !this->IsVarArg()) + { + if (flags & (STRUCT_FLOAT_FIELD_FIRST | STRUCT_FLOAT_FIELD_SECOND)) + { + assert(cFPRegs == 1); + assert((STRUCT_FLOAT_FIELD_FIRST == (flags & STRUCT_HAS_FLOAT_FIELDS_MASK)) || (STRUCT_FLOAT_FIELD_SECOND == (flags & STRUCT_HAS_FLOAT_FIELDS_MASK))); + + if ((1 + m_idxFPReg <= NUM_ARGUMENT_REGISTERS) && (m_idxGenReg + 1 <= NUM_ARGUMENT_REGISTERS)) + { + m_argLocDescForStructInRegs.Init(); + m_argLocDescForStructInRegs.m_idxFloatReg = m_idxFPReg; + m_argLocDescForStructInRegs.m_cFloatReg = 1; + int argOfs = TransitionBlock::GetOffsetOfFloatArgumentRegisters() + m_idxFPReg * 8; + m_idxFPReg += 1; + + m_argLocDescForStructInRegs.m_structFields = flags; + + m_argLocDescForStructInRegs.m_idxGenReg = m_idxGenReg; + m_argLocDescForStructInRegs.m_cGenReg = 1; + m_idxGenReg += 1; + + m_hasArgLocDescForStructInRegs = true; + + return argOfs; + } + } + else if (cFPRegs + m_idxFPReg <= NUM_ARGUMENT_REGISTERS) + { + int argOfs = TransitionBlock::GetOffsetOfFloatArgumentRegisters() + m_idxFPReg * 8; + if (flags == STRUCT_FLOAT_FIELD_ONLY_TWO) // struct with two float-fields. + { + m_argLocDescForStructInRegs.Init(); + m_hasArgLocDescForStructInRegs = true; + m_argLocDescForStructInRegs.m_idxFloatReg = m_idxFPReg; + assert(cFPRegs == 2); + m_argLocDescForStructInRegs.m_cFloatReg = 2; + assert(argSize == 8); + m_argLocDescForStructInRegs.m_structFields = STRUCT_FLOAT_FIELD_ONLY_TWO; + } + m_idxFPReg += cFPRegs; + return argOfs; + } + } + + { + const int regSlots = ALIGN_UP(cbArg, TARGET_POINTER_SIZE) / TARGET_POINTER_SIZE; + if (m_idxGenReg + regSlots <= NUM_ARGUMENT_REGISTERS) + { + int argOfs = TransitionBlock::GetOffsetOfArgumentRegisters() + m_idxGenReg * 8; + m_idxGenReg += regSlots; + return argOfs; + } + else if (m_idxGenReg < NUM_ARGUMENT_REGISTERS) + { + int argOfs = TransitionBlock::GetOffsetOfArgumentRegisters() + m_idxGenReg * 8; + m_ofsStack += (m_idxGenReg + regSlots - NUM_ARGUMENT_REGISTERS)*8; + assert(m_ofsStack == 8); + m_idxGenReg = NUM_ARGUMENT_REGISTERS; + return argOfs; + } + } + + int argOfs = TransitionBlock::GetOffsetOfArgs() + m_ofsStack; + m_ofsStack += ALIGN_UP(cbArg, TARGET_POINTER_SIZE); + return argOfs; #else PORTABILITY_ASSERT("ArgIteratorTemplate::GetNextOffset"); @@ -1786,7 +2002,7 @@ void ArgIteratorTemplate::ComputeReturnFlags() break; case ELEMENT_TYPE_R4: -#if defined(TARGET_LOONGARCH64) +#if defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64) flags |= STRUCT_FLOAT_FIELD_ONLY_ONE << RETURN_FP_SIZE_SHIFT; #else #ifndef ARM_SOFTFP @@ -1796,7 +2012,7 @@ void ArgIteratorTemplate::ComputeReturnFlags() break; case ELEMENT_TYPE_R8: -#if defined(TARGET_LOONGARCH64) +#if defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64) flags |= (STRUCT_FLOAT_FIELD_ONLY_ONE | STRUCT_FIRST_FIELD_SIZE_IS8) << RETURN_FP_SIZE_SHIFT; #else #ifndef ARM_SOFTFP @@ -1875,6 +2091,15 @@ void ArgIteratorTemplate::ComputeReturnFlags() flags = (MethodTable::GetLoongArch64PassStructInRegisterFlags((CORINFO_CLASS_HANDLE)pMethodTable) & 0xff) << RETURN_FP_SIZE_SHIFT; break; } +#elif defined(TARGET_RISCV64) + if (size <= ENREGISTERED_RETURNTYPE_INTEGER_MAXSIZE) + { + assert(!thValueType.IsTypeDesc()); + + MethodTable *pMethodTable = thValueType.AsMethodTable(); + flags = (MethodTable::GetRiscv64PassStructInRegisterFlags((CORINFO_CLASS_HANDLE)pMethodTable) & 0xff) << RETURN_FP_SIZE_SHIFT; + break; + } #else if (size <= ENREGISTERED_RETURNTYPE_INTEGER_MAXSIZE) break; diff --git a/src/coreclr/vm/ceeload.h b/src/coreclr/vm/ceeload.h index c81a5a226bb8bc..b2a31352746e0f 100644 --- a/src/coreclr/vm/ceeload.h +++ b/src/coreclr/vm/ceeload.h @@ -87,6 +87,8 @@ class JITInlineTrackingMap; #define NATIVE_SYMBOL_READER_DLL W("Microsoft.DiaSymReader.Native.arm64.dll") #elif defined(HOST_LOONGARCH64) #define NATIVE_SYMBOL_READER_DLL W("Microsoft.DiaSymReader.Native.loongarch64.dll") +#elif defined(HOST_RISCV64) +#define NATIVE_SYMBOL_READER_DLL W("Microsoft.DiaSymReader.Native.riscv64.dll") #endif typedef DPTR(JITInlineTrackingMap) PTR_JITInlineTrackingMap; diff --git a/src/coreclr/vm/codeman.cpp b/src/coreclr/vm/codeman.cpp index b3f10b5c098aa2..7080d2296a8d6a 100644 --- a/src/coreclr/vm/codeman.cpp +++ b/src/coreclr/vm/codeman.cpp @@ -807,7 +807,7 @@ ExecutionManager::DeleteRange //----------------------------------------------------------------------------- -#if defined(TARGET_ARM) || defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64) +#if defined(TARGET_ARM) || defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64) #define EXCEPTION_DATA_SUPPORTS_FUNCTION_FRAGMENTS #endif @@ -911,6 +911,29 @@ BOOL IsFunctionFragment(TADDR baseAddress, PTR_RUNTIME_FUNCTION pFunctionEntry) pUnwindCodes += EpilogCount; } + return ((*pUnwindCodes & 0xFF) == 0xE5); +#elif defined(TARGET_RISCV64) + int EpilogCount = (int)(unwindHeader >> 22) & 0x1F; + int CodeWords = unwindHeader >> 27; + PTR_DWORD pUnwindCodes = (PTR_DWORD)(baseAddress + pFunctionEntry->UnwindData); + // Skip header. + pUnwindCodes++; + + // Skip extended header. + if ((CodeWords == 0) && (EpilogCount == 0)) + { + EpilogCount = (*pUnwindCodes) & 0xFFFF; + pUnwindCodes++; + } + + // Skip epilog scopes. + BOOL Ebit = (unwindHeader >> 21) & 0x1; + if (!Ebit && (EpilogCount != 0)) + { + // EpilogCount is the number of exception scopes defined right after the unwindHeader + pUnwindCodes += EpilogCount; + } + return ((*pUnwindCodes & 0xFF) == 0xE5); #else PORTABILITY_ASSERT("IsFunctionFragnent - NYI on this platform"); @@ -1115,6 +1138,44 @@ PTR_VOID GetUnwindDataBlob(TADDR moduleBase, PTR_RUNTIME_FUNCTION pRuntimeFuncti *pSize = size; return xdata; +#elif defined(TARGET_RISCV64) + // TODO: maybe optimize further. + // if this function uses packed unwind data then at least one of the two least significant bits + // will be non-zero. if this is the case then there will be no xdata record to enumerate. + _ASSERTE((pRuntimeFunction->UnwindData & 0x3) == 0); + + // compute the size of the unwind info + PTR_ULONG xdata = dac_cast(pRuntimeFunction->UnwindData + moduleBase); + ULONG epilogScopes = 0; + ULONG unwindWords = 0; + ULONG size = 0; + + //If both Epilog Count and Code Word is not zero + //Info of Epilog and Unwind scopes are given by 1 word header + //Otherwise this info is given by a 2 word header + if ((xdata[0] >> 27) != 0) + { + size = 4; + epilogScopes = (xdata[0] >> 22) & 0x1f; + unwindWords = (xdata[0] >> 27) & 0x1f; + } + else + { + size = 8; + epilogScopes = xdata[1] & 0xffff; + unwindWords = (xdata[1] >> 16) & 0xff; + } + + if (!(xdata[0] & (1 << 21))) + size += 4 * epilogScopes; + + size += 4 * unwindWords; + + _ASSERTE(xdata[0] & (1 << 20)); // personality routine should be always present + size += 4; // exception handler RVA + + *pSize = size; + return xdata; #else PORTABILITY_ASSERT("GetUnwindDataBlob"); @@ -2246,6 +2307,8 @@ BOOL EEJitManager::LoadJIT() altJitName = MAKEDLLNAME_W(W("clrjit_unix_x64_x64")); #elif defined(TARGET_LOONGARCH64) altJitName = MAKEDLLNAME_W(W("clrjit_unix_loongarch64_loongarch64")); +#elif defined(TARGET_RISCV64) + altJitName = MAKEDLLNAME_W(W("clrjit_unix_riscv64_riscv64")); #endif #endif // TARGET_WINDOWS @@ -2634,7 +2697,7 @@ static size_t GetDefaultReserveForJumpStubs(size_t codeHeapSize) { LIMITED_METHOD_CONTRACT; -#if defined(TARGET_AMD64) || defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64) +#if defined(TARGET_AMD64) || defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64) // // Keep a small default reserve at the end of the codeheap for jump stubs. It should reduce // chance that we won't be able allocate jump stub because of lack of suitable address space. @@ -2686,7 +2749,7 @@ HeapList* LoaderCodeHeap::CreateCodeHeap(CodeHeapRequestInfo *pInfo, LoaderHeap bool fAllocatedFromEmergencyJumpStubReserve = false; size_t allocationSize = pCodeHeap->m_LoaderHeap.AllocMem_TotalSize(initialRequestSize); -#if defined(TARGET_AMD64) || defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64) +#if defined(TARGET_AMD64) || defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64) allocationSize += pCodeHeap->m_LoaderHeap.AllocMem_TotalSize(JUMP_ALLOCATE_SIZE); #endif pBaseAddr = (BYTE *)pInfo->m_pAllocator->GetCodeHeapInitialBlock(loAddr, hiAddr, (DWORD)allocationSize, &dwSizeAcquiredFromInitialBlock); @@ -2733,7 +2796,7 @@ HeapList* LoaderCodeHeap::CreateCodeHeap(CodeHeapRequestInfo *pInfo, LoaderHeap // this first allocation is critical as it sets up correctly the loader heap info HeapList *pHp = new HeapList; -#if defined(TARGET_AMD64) || defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64) +#if defined(TARGET_AMD64) || defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64) pHp->CLRPersonalityRoutine = (BYTE *)pCodeHeap->m_LoaderHeap.AllocMem(JUMP_ALLOCATE_SIZE); #else // Ensure that the heap has a reserved block of memory and so the GetReservedBytesFree() @@ -2886,7 +2949,7 @@ HeapList* EEJitManager::NewCodeHeap(CodeHeapRequestInfo *pInfo, DomainCodeHeapLi size_t reserveSize = initialRequestSize; -#if defined(TARGET_AMD64) || defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64) +#if defined(TARGET_AMD64) || defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64) reserveSize += JUMP_ALLOCATE_SIZE; #endif @@ -4442,7 +4505,7 @@ PTR_RUNTIME_FUNCTION EEJitManager::LazyGetFunctionEntry(EECodeInfo * pCodeInfo) if (RUNTIME_FUNCTION__BeginAddress(pFunctionEntry) <= address && address < RUNTIME_FUNCTION__EndAddress(pFunctionEntry, baseAddress)) { -#if defined(EXCEPTION_DATA_SUPPORTS_FUNCTION_FRAGMENTS) && (defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64)) +#if defined(EXCEPTION_DATA_SUPPORTS_FUNCTION_FRAGMENTS) && (defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64)) // If we might have fragmented unwind, and we're on ARM64/LoongArch64, // make sure to returning the root record, // as the trailing records don't have prolog unwind codes. diff --git a/src/coreclr/vm/codeman.h b/src/coreclr/vm/codeman.h index 5c1ec4230b4d92..394e0c2ffcf194 100644 --- a/src/coreclr/vm/codeman.h +++ b/src/coreclr/vm/codeman.h @@ -480,13 +480,13 @@ struct HeapList size_t maxCodeHeapSize;// Size of the entire contiguous block of memory size_t reserveForJumpStubs; // Amount of memory reserved for jump stubs in this block -#if defined(TARGET_AMD64) || defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64) +#if defined(TARGET_AMD64) || defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64) BYTE* CLRPersonalityRoutine; // jump thunk to personality routine #endif TADDR GetModuleBase() { -#if defined(TARGET_AMD64) || defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64) +#if defined(TARGET_AMD64) || defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64) return (TADDR)CLRPersonalityRoutine; #else return (TADDR)mapBase; diff --git a/src/coreclr/vm/dynamicmethod.cpp b/src/coreclr/vm/dynamicmethod.cpp index 5a140d6ed0a13d..897adc463c82fd 100644 --- a/src/coreclr/vm/dynamicmethod.cpp +++ b/src/coreclr/vm/dynamicmethod.cpp @@ -437,7 +437,7 @@ HeapList* HostCodeHeap::InitializeHeapList(CodeHeapRequestInfo *pInfo) TrackAllocation *pTracker = NULL; -#if defined(TARGET_AMD64) || defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64) +#if defined(TARGET_AMD64) || defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64) pTracker = AllocMemory_NoThrow(0, JUMP_ALLOCATE_SIZE, sizeof(void*), 0); if (pTracker == NULL) diff --git a/src/coreclr/vm/eetwain.cpp b/src/coreclr/vm/eetwain.cpp index 640591c7d9b71f..1e16cb811bc6f9 100644 --- a/src/coreclr/vm/eetwain.cpp +++ b/src/coreclr/vm/eetwain.cpp @@ -1486,7 +1486,7 @@ bool EECodeManager::IsGcSafe( EECodeInfo *pCodeInfo, return gcInfoDecoder.IsInterruptible(); } -#if defined(TARGET_ARM) || defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64) +#if defined(TARGET_ARM) || defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64) bool EECodeManager::HasTailCalls( EECodeInfo *pCodeInfo) { CONTRACTL { @@ -1504,7 +1504,7 @@ bool EECodeManager::HasTailCalls( EECodeInfo *pCodeInfo) return gcInfoDecoder.HasTailCalls(); } -#endif // TARGET_ARM || TARGET_ARM64 || TARGET_LOONGARCH64 +#endif // TARGET_ARM || TARGET_ARM64 || TARGET_LOONGARCH64 || TARGET_RISCV64 #if defined(TARGET_AMD64) && defined(_DEBUG) diff --git a/src/coreclr/vm/encee.cpp b/src/coreclr/vm/encee.cpp index 78ac8b9abd2f55..9b83cd39d92407 100644 --- a/src/coreclr/vm/encee.cpp +++ b/src/coreclr/vm/encee.cpp @@ -608,7 +608,7 @@ HRESULT EditAndContinueModule::ResumeInUpdatedFunction( SIZE_T newILOffset, CONTEXT *pOrigContext) { -#if defined(TARGET_ARM) || defined(TARGET_LOONGARCH64) +#if defined(TARGET_ARM) || defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64) return E_NOTIMPL; #else LOG((LF_ENC, LL_INFO100, "EnCModule::ResumeInUpdatedFunction for %s at IL offset 0x%x, ", diff --git a/src/coreclr/vm/excep.cpp b/src/coreclr/vm/excep.cpp index ac867bd6123fd2..620b4362492688 100644 --- a/src/coreclr/vm/excep.cpp +++ b/src/coreclr/vm/excep.cpp @@ -6280,9 +6280,9 @@ IsDebuggerFault(EXCEPTION_RECORD *pExceptionRecord, #endif // TARGET_UNIX -#if !defined(TARGET_ARM64) && !defined(TARGET_LOONGARCH64) +#if !defined(TARGET_ARM64) && !defined(TARGET_LOONGARCH64) && !defined(TARGET_RISCV64) EXTERN_C void JIT_StackProbe_End(); -#endif // TARGET_ARM64 +#endif // !TARGET_ARM64 && !TARGET_LOONGARCH64 && !TARGET_RISCV64 #ifdef FEATURE_EH_FUNCLETS @@ -6347,9 +6347,9 @@ bool IsIPInMarkedJitHelper(UINT_PTR uControlPc) CHECK_RANGE(JIT_WriteBarrier) CHECK_RANGE(JIT_CheckedWriteBarrier) CHECK_RANGE(JIT_ByRefWriteBarrier) -#if !defined(TARGET_ARM64) && !defined(TARGET_LOONGARCH64) +#if !defined(TARGET_ARM64) && !defined(TARGET_LOONGARCH64) && !(TARGET_RISCV64) CHECK_RANGE(JIT_StackProbe) -#endif // !TARGET_ARM64 +#endif // !TARGET_ARM64 && !TARGET_LOONGARCH64 && !TARGET_RISCV64 #else #ifdef TARGET_UNIX CHECK_RANGE(JIT_WriteBarrierGroup) @@ -6471,7 +6471,7 @@ AdjustContextForJITHelpers( Thread::VirtualUnwindToFirstManagedCallFrame(pContext); -#if defined(TARGET_ARM) || defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64) +#if defined(TARGET_ARM) || defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64) // We had an AV in the writebarrier that needs to be treated // as originating in managed code. At this point, the stack (growing // from left->right) looks like this: @@ -6495,7 +6495,7 @@ AdjustContextForJITHelpers( // Now we save the address back into the context so that it gets used // as the faulting address. SetIP(pContext, ControlPCPostAdjustment); -#endif // TARGET_ARM || TARGET_ARM64 || TARGET_LOONGARCH64 +#endif // TARGET_ARM || TARGET_ARM64 || TARGET_LOONGARCH64 || TARGET_RISCV64 // Unwind the frame chain - On Win64, this is required since we may handle the managed fault and to do so, // we will replace the exception context with the managed context and "continue execution" there. Thus, we do not diff --git a/src/coreclr/vm/fieldmarshaler.cpp b/src/coreclr/vm/fieldmarshaler.cpp index ea0bbb871cf4fc..3be82af9fcd4cc 100644 --- a/src/coreclr/vm/fieldmarshaler.cpp +++ b/src/coreclr/vm/fieldmarshaler.cpp @@ -421,6 +421,7 @@ UINT32 NativeFieldDescriptor::AlignmentRequirement() const } } +#if 0 PTR_MethodTable NativeFieldDescriptor::GetNestedNativeMethodTable() const { CONTRACT(PTR_MethodTable) @@ -435,6 +436,7 @@ PTR_MethodTable NativeFieldDescriptor::GetNestedNativeMethodTable() const RETURN nestedTypeAndCount.m_pNestedType; } +#endif PTR_FieldDesc NativeFieldDescriptor::GetFieldDesc() const { diff --git a/src/coreclr/vm/fieldmarshaler.h b/src/coreclr/vm/fieldmarshaler.h index 83b2c79fb4f055..80ef481ca8fdd0 100644 --- a/src/coreclr/vm/fieldmarshaler.h +++ b/src/coreclr/vm/fieldmarshaler.h @@ -92,7 +92,20 @@ class NativeFieldDescriptor return m_category; } - PTR_MethodTable GetNestedNativeMethodTable() const; + PTR_MethodTable GetNestedNativeMethodTable() const + { + CONTRACT(PTR_MethodTable) + { + NOTHROW; + GC_NOTRIGGER; + MODE_ANY; + PRECONDITION(IsNestedType()); + POSTCONDITION(CheckPointer(RETVAL)); + } + CONTRACT_END; + + RETURN nestedTypeAndCount.m_pNestedType; + } ULONG GetNumElements() const { diff --git a/src/coreclr/vm/frames.h b/src/coreclr/vm/frames.h index 2261fe4431be1c..83c218b149bad8 100644 --- a/src/coreclr/vm/frames.h +++ b/src/coreclr/vm/frames.h @@ -863,6 +863,9 @@ class RedirectedThreadFrame : public ResumableFrame #elif defined(TARGET_LOONGARCH64) Object** firstIntReg = (Object**)&this->GetContext()->Tp; Object** lastIntReg = (Object**)&this->GetContext()->S8; +#elif defined(TARGET_RISCV64) + Object** firstIntReg = (Object**)&this->GetContext()->Gp; + Object** lastIntReg = (Object**)&this->GetContext()->T6; #else _ASSERTE(!"nyi for platform"); #endif @@ -1904,7 +1907,7 @@ class UnmanagedToManagedFrame : public Frame TADDR m_ReturnAddress; TADDR m_x8; // ret buff arg ArgumentRegisters m_argumentRegisters; -#elif defined (TARGET_LOONGARCH64) +#elif defined (TARGET_LOONGARCH64) || defined (TARGET_RISCV64) TADDR m_fp; TADDR m_ReturnAddress; ArgumentRegisters m_argumentRegisters; diff --git a/src/coreclr/vm/gccover.cpp b/src/coreclr/vm/gccover.cpp index 67cf677273f759..0b2592ad763a44 100644 --- a/src/coreclr/vm/gccover.cpp +++ b/src/coreclr/vm/gccover.cpp @@ -36,7 +36,7 @@ MethodDesc* AsMethodDesc(size_t addr); static PBYTE getTargetOfCall(PBYTE instrPtr, PCONTEXT regs, PBYTE*nextInstr); -#if defined(TARGET_ARM) || defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64) +#if defined(TARGET_ARM) || defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64) static void replaceSafePointInstructionWithGcStressInstr(UINT32 safePointOffset, LPVOID codeStart); static bool replaceInterruptibleRangesWithGcStressInstr (UINT32 startOffset, UINT32 stopOffset, LPVOID codeStart); #endif @@ -99,7 +99,7 @@ bool IsGcCoverageInterruptInstruction(PBYTE instrPtr) { UINT32 instrVal; -#if defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64) +#if defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64) instrVal = *reinterpret_cast(instrPtr); #elif defined(TARGET_ARM) size_t instrLen = GetARMInstructionLength(instrPtr); @@ -120,7 +120,7 @@ bool IsGcCoverageInterruptInstruction(PBYTE instrPtr) bool IsOriginalInstruction(PBYTE instrPtr, GCCoverageInfo* gcCover, DWORD offset) { -#if defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64) +#if defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64) UINT32 instrVal = *reinterpret_cast(instrPtr); UINT32 origInstrVal = *reinterpret_cast(gcCover->savedCode + offset); return (instrVal == origInstrVal); @@ -176,7 +176,7 @@ void SetupAndSprinkleBreakpoints( fZapped); // This is not required for ARM* as the above call does the work for both hot & cold regions -#if !defined(TARGET_ARM) && !defined(TARGET_ARM64) && !defined(TARGET_LOONGARCH64) +#if !defined(TARGET_ARM) && !defined(TARGET_ARM64) && !defined(TARGET_LOONGARCH64) && !defined(TARGET_RISCV64) if (gcCover->methodRegion.coldSize != 0) { gcCover->SprinkleBreakpoints(gcCover->savedCode + gcCover->methodRegion.hotSize, @@ -343,6 +343,8 @@ void ReplaceInstrAfterCall(PBYTE instrToReplace, MethodDesc* callMD) *(DWORD*)instrToReplace = INTERRUPT_INSTR_PROTECT_RET; else *(DWORD*)instrToReplace = INTERRUPT_INSTR; +#elif defined(TARGET_RISCV64) + _ASSERTE(!"not implemented for RISCV64 NYI"); #else _ASSERTE(!"not implemented for platform"); #endif @@ -626,7 +628,7 @@ void GCCoverageInfo::SprinkleBreakpoints( if ((regionOffsetAdj==0) && (*codeStart != INTERRUPT_INSTR)) doingEpilogChecks = false; -#elif defined(TARGET_ARM) || defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64) +#elif defined(TARGET_ARM) || defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64) //Save the method code from hotRegion memcpy(saveAddr, (BYTE*)methodRegion.hotStartAddress, methodRegion.hotSize); @@ -670,7 +672,7 @@ void GCCoverageInfo::SprinkleBreakpoints( #endif // TARGET_X86 } -#if defined(TARGET_ARM) || defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64) +#if defined(TARGET_ARM) || defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64) #ifdef PARTIALLY_INTERRUPTIBLE_GC_SUPPORTED @@ -771,6 +773,8 @@ void replaceSafePointInstructionWithGcStressInstr(UINT32 safePointOffset, LPVOID { instructionIsACallThroughRegister = TRUE; } +#elif defined(TARGET_RISCV64) + _ASSERTE(!"not implemented for RISCV64 NYI"); #endif // _TARGET_XXXX_ // safe point must always be after a call instruction @@ -794,7 +798,7 @@ void replaceSafePointInstructionWithGcStressInstr(UINT32 safePointOffset, LPVOID // safe point will be replaced with appropriate illegal instruction at execution time when reg value is known #if defined(TARGET_ARM) *((WORD*)instrPtrWriterHolder.GetRW()) = INTERRUPT_INSTR_CALL; -#elif defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64) +#elif defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64) *((DWORD*)instrPtrWriterHolder.GetRW()) = INTERRUPT_INSTR_CALL; #endif // _TARGET_XXXX_ } @@ -915,7 +919,7 @@ bool replaceInterruptibleRangesWithGcStressInstr (UINT32 startOffset, UINT32 sto } instrPtrRW += instrLen; -#elif defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64) +#elif defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64) *((DWORD*)instrPtrRW) = INTERRUPT_INSTR; instrPtrRW += 4; #endif // TARGET_XXXX_ @@ -1017,6 +1021,8 @@ static PBYTE getTargetOfCall(PBYTE instrPtr, PCONTEXT regs, PBYTE* nextInstr) { { return 0; // Fail } +#elif defined(TARGET_RISCV64) + _ASSERTE(!"not implemented for RISCV64 NYI"); #endif #ifdef TARGET_AMD64 @@ -1258,6 +1264,8 @@ void RemoveGcCoverageInterrupt(TADDR instrPtr, BYTE * savedInstrPtr, GCCoverageI *(DWORD *)instrPtrWriterHolder.GetRW() = *(DWORD *)savedInstrPtr; #elif defined(TARGET_LOONGARCH64) *(DWORD *)instrPtrWriterHolder.GetRW() = *(DWORD *)savedInstrPtr; +#elif defined(TARGET_RISCV64) + *(DWORD *)instrPtrWriterHolder.GetRW() = *(DWORD *)savedInstrPtr; #else *(BYTE *)instrPtrWriterHolder.GetRW() = *savedInstrPtr; #endif @@ -1478,6 +1486,13 @@ void DoGcStress (PCONTEXT regs, NativeCodeVersion nativeCodeVersion) DWORD instrVal = *(DWORD *)instrPtr; forceStack[6] = &instrVal; // This is so I can see it fastchecked + atCall = (instrVal == INTERRUPT_INSTR_CALL); + afterCallProtect[0] = (instrVal == INTERRUPT_INSTR_PROTECT_RET); +#elif defined(TARGET_RISCV64) + _ASSERTE(!"not implemented for RISCV64 NYI"); + DWORD instrVal = *(DWORD *)instrPtr; + forceStack[6] = &instrVal; // This is so I can see it fastchecked + atCall = (instrVal == INTERRUPT_INSTR_CALL); afterCallProtect[0] = (instrVal == INTERRUPT_INSTR_PROTECT_RET); #endif // _TARGET_* @@ -1598,7 +1613,7 @@ void DoGcStress (PCONTEXT regs, NativeCodeVersion nativeCodeVersion) } #endif // TARGET_X86 -#if defined(TARGET_X86) || defined(TARGET_AMD64) || defined(TARGET_ARM) || defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64) +#if defined(TARGET_X86) || defined(TARGET_AMD64) || defined(TARGET_ARM) || defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64) /* In non-fully interruptible code, if the EIP is just after a call instr means something different because it expects that we are IN the @@ -1654,6 +1669,9 @@ void DoGcStress (PCONTEXT regs, NativeCodeVersion nativeCodeVersion) *(DWORD*)nextInstrWriterHolder.GetRW() = INTERRUPT_INSTR; #elif defined(TARGET_LOONGARCH64) *(DWORD*)nextInstrWriterHolder.GetRW() = INTERRUPT_INSTR; +#elif defined(TARGET_RISCV64) + _ASSERTE(!"TODO RISCV64 NYI"); + *(DWORD*)nextInstrWriterHolder.GetRW() = INTERRUPT_INSTR; #else *nextInstrWriterHolder.GetRW() = INTERRUPT_INSTR; #endif @@ -1736,6 +1754,8 @@ void DoGcStress (PCONTEXT regs, NativeCodeVersion nativeCodeVersion) retValRegs[numberOfRegs++] = regs->X0; #elif defined(TARGET_LOONGARCH64) retValRegs[numberOfRegs++] = regs->A0; +#elif defined(TARGET_RISCV64) + retValRegs[numberOfRegs++] = regs->A0; #endif // TARGET_ARM64 } @@ -1789,6 +1809,8 @@ void DoGcStress (PCONTEXT regs, NativeCodeVersion nativeCodeVersion) regs->X[0] = retValRegs[0]; #elif defined(TARGET_LOONGARCH64) regs->A0 = retValRegs[0]; +#elif defined(TARGET_RISCV64) + regs->A0 = retValRegs[0]; #else PORTABILITY_ASSERT("DoGCStress - return register"); #endif diff --git a/src/coreclr/vm/gccover.h b/src/coreclr/vm/gccover.h index 35207372671d78..3f0a04bcac5a9e 100644 --- a/src/coreclr/vm/gccover.h +++ b/src/coreclr/vm/gccover.h @@ -112,6 +112,12 @@ typedef DPTR(GCCoverageInfo) PTR_GCCoverageInfo; // see code:GCCoverageInfo::sav #define INTERRUPT_INSTR_CALL 0xffffff0e #define INTERRUPT_INSTR_PROTECT_RET 0xffffff0d +#elif defined(TARGET_RISCV64) +// TODO RISCV64 NYI +#define INTERRUPT_INSTR 0xBADC0DE0 +#define INTERRUPT_INSTR_CALL 0xBADC0DE1 +#define INTERRUPT_INSTR_PROTECT_RET 0xBADC0DE2 + #endif // _TARGET_* // The body of this method is in this header file to allow @@ -174,6 +180,9 @@ inline bool IsGcCoverageInterruptInstructionVal(UINT32 instrVal) return false; } } +#elif defined(TARGET_RISCV64) + _ASSERTE(!"TODO RISCV64 NYI"); + return false; #else // x64 and x86 diff --git a/src/coreclr/vm/gcenv.ee.cpp b/src/coreclr/vm/gcenv.ee.cpp index 1ecb498794c4f6..7d9843e062bf07 100644 --- a/src/coreclr/vm/gcenv.ee.cpp +++ b/src/coreclr/vm/gcenv.ee.cpp @@ -927,7 +927,7 @@ void GCToEEInterface::StompWriteBarrier(WriteBarrierParameters* args) // On architectures with strong ordering, we only need to prevent compiler reordering. // Otherwise we put a process-wide fence here (so that we could use an ordinary read in the barrier) -#if defined(HOST_ARM64) || defined(HOST_ARM) || defined(HOST_LOONGARCH64) +#if defined(HOST_ARM64) || defined(HOST_ARM) || defined(HOST_LOONGARCH64) || defined(HOST_RISCV64) if (!is_runtime_suspended) { // If runtime is not suspended, force all threads to see the changed table before seeing updated heap boundaries. @@ -939,7 +939,7 @@ void GCToEEInterface::StompWriteBarrier(WriteBarrierParameters* args) g_lowest_address = args->lowest_address; g_highest_address = args->highest_address; -#if defined(HOST_ARM64) || defined(HOST_ARM) || defined(HOST_LOONGARCH64) +#if defined(HOST_ARM64) || defined(HOST_ARM) || defined(HOST_LOONGARCH64) || defined(HOST_RISCV64) // Need to reupdate for changes to g_highest_address g_lowest_address stompWBCompleteActions |= ::StompWriteBarrierResize(is_runtime_suspended, args->requires_upper_bounds_check); @@ -979,7 +979,7 @@ void GCToEEInterface::StompWriteBarrier(WriteBarrierParameters* args) // (we care only about managed threads and suspend/resume will do full fences - good enough for us). // -#if defined(HOST_ARM64) || defined(HOST_ARM) || defined(HOST_LOONGARCH64) +#if defined(HOST_ARM64) || defined(HOST_ARM) || defined(HOST_LOONGARCH64) || defined(HOST_RISCV64) is_runtime_suspended = (stompWBCompleteActions & SWB_EE_RESTART) || is_runtime_suspended; if (!is_runtime_suspended) { diff --git a/src/coreclr/vm/gcinfodecoder.cpp b/src/coreclr/vm/gcinfodecoder.cpp index 67fbb3b25e2548..4acd0b12121e03 100644 --- a/src/coreclr/vm/gcinfodecoder.cpp +++ b/src/coreclr/vm/gcinfodecoder.cpp @@ -133,7 +133,7 @@ GcInfoDecoder::GcInfoDecoder( int hasStackBaseRegister = headerFlags & GC_INFO_HAS_STACK_BASE_REGISTER; #ifdef TARGET_AMD64 m_WantsReportOnlyLeaf = ((headerFlags & GC_INFO_WANTS_REPORT_ONLY_LEAF) != 0); -#elif defined(TARGET_ARM) || defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64) +#elif defined(TARGET_ARM) || defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64) m_HasTailCalls = ((headerFlags & GC_INFO_HAS_TAILCALLS) != 0); #endif // TARGET_AMD64 int hasEncInfo = headerFlags & GC_INFO_HAS_EDIT_AND_CONTINUE_INFO; @@ -144,7 +144,7 @@ GcInfoDecoder::GcInfoDecoder( (ReturnKind)((UINT32)m_Reader.Read(returnKindBits)); remainingFlags &= ~(DECODE_RETURN_KIND | DECODE_VARARG); -#if defined(TARGET_ARM) || defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64) +#if defined(TARGET_ARM) || defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64) remainingFlags &= ~DECODE_HAS_TAILCALLS; #endif if (remainingFlags == 0) @@ -383,7 +383,7 @@ bool GcInfoDecoder::IsSafePoint(UINT32 codeOffset) if(m_NumSafePoints == 0) return false; -#if defined(TARGET_AMD64) || defined(TARGET_ARM) || defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64) +#if defined(TARGET_AMD64) || defined(TARGET_ARM) || defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64) // Safepoints are encoded with a -1 adjustment codeOffset--; #endif @@ -403,7 +403,7 @@ UINT32 GcInfoDecoder::FindSafePoint(UINT32 breakOffset) const UINT32 numBitsPerOffset = CeilOfLog2(NORMALIZE_CODE_OFFSET(m_CodeLength)); UINT32 result = m_NumSafePoints; -#if defined(TARGET_ARM) || defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64) +#if defined(TARGET_ARM) || defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64) // Safepoints are encoded with a -1 adjustment // but normalizing them masks off the low order bit // Thus only bother looking if the address is odd @@ -450,7 +450,7 @@ void GcInfoDecoder::EnumerateSafePoints(EnumerateSafePointsCallback *pCallback, UINT32 normOffset = (UINT32)m_Reader.Read(numBitsPerOffset); UINT32 offset = DENORMALIZE_CODE_OFFSET(normOffset) + 2; -#if defined(TARGET_AMD64) || defined(TARGET_ARM) || defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64) +#if defined(TARGET_AMD64) || defined(TARGET_ARM) || defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64) // Safepoints are encoded with a -1 adjustment offset--; #endif @@ -536,13 +536,13 @@ bool GcInfoDecoder::GetIsVarArg() return m_IsVarArg; } -#if defined(TARGET_ARM) || defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64) +#if defined(TARGET_ARM) || defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64) bool GcInfoDecoder::HasTailCalls() { _ASSERTE( m_Flags & DECODE_HAS_TAILCALLS ); return m_HasTailCalls; } -#endif // TARGET_ARM || TARGET_ARM64 || TARGET_LOONGARCH64 +#endif // TARGET_ARM || TARGET_ARM64 || TARGET_LOONGARCH64 || TARGET_RISCV64 bool GcInfoDecoder::WantsReportOnlyLeaf() { @@ -1893,6 +1893,144 @@ void GcInfoDecoder::ReportRegisterToGC( pCallBack(hCallBack, pObjRef, gcFlags DAC_ARG(DacSlotLocation(regNum, 0, false))); } +#elif defined(TARGET_RISCV64) + +#if defined(TARGET_UNIX) && !defined(FEATURE_NATIVEAOT) +OBJECTREF* GcInfoDecoder::GetCapturedRegister( + int regNum, + PREGDISPLAY pRD + ) +{ + _ASSERTE(regNum >= 1 && regNum <= 31); + + // The fields of CONTEXT are in the same order as + // the processor encoding numbers. + + DWORD64 *pR0 = &pRD->pCurrentContext->R0; + + return (OBJECTREF*)(pR0 + regNum); +} +#endif // TARGET_UNIX && !FEATURE_NATIVEAOT + +OBJECTREF* GcInfoDecoder::GetRegisterSlot( + int regNum, + PREGDISPLAY pRD + ) +{ + _ASSERTE((regNum == 1) || (regNum >= 5 && regNum <= 31)); + +#ifdef FEATURE_NATIVEAOT + PTR_UIntNative* ppReg = &pRD->pR0; + + return (OBJECTREF*)*(ppReg + regNum); +#else + if(regNum == 1) + { + return (OBJECTREF*) pRD->pCurrentContextPointers->Ra; + } + else if (regNum < 8) + { + return (OBJECTREF*)*(DWORD64**)(&pRD->volatileCurrContextPointers.T0 + (regNum - 5)); + } + else if(regNum == 8) + { + return (OBJECTREF*) pRD->pCurrentContextPointers->Fp; + } + else if (regNum == 9) + { + return (OBJECTREF*) pRD->pCurrentContextPointers->S1; + } + else if (regNum < 18) + { + return (OBJECTREF*)*(DWORD64**)(&pRD->volatileCurrContextPointers.A0 + (regNum - 10)); + } + else if (regNum < 28) + { + return (OBJECTREF*)*(DWORD64**)(&pRD->pCurrentContextPointers->S2 + (regNum-18)); + } + return (OBJECTREF*)*(DWORD64**)(&pRD->volatileCurrContextPointers.T3 + (regNum-28)); +#endif +} + +bool GcInfoDecoder::IsScratchRegister(int regNum, PREGDISPLAY pRD) +{ + _ASSERTE(regNum >= 0 && regNum <= 31); + + return (regNum >= 5 && regNum <= 7) || (regNum >= 10 and regNum <= 17) || regNum >= 28 || regNum == 1; +} + +bool GcInfoDecoder::IsScratchStackSlot(INT32 spOffset, GcStackSlotBase spBase, PREGDISPLAY pRD) +{ +#ifdef FIXED_STACK_PARAMETER_SCRATCH_AREA + _ASSERTE( m_Flags & DECODE_GC_LIFETIMES ); + + TADDR pSlot = (TADDR) GetStackSlot(spOffset, spBase, pRD); + _ASSERTE(pSlot >= pRD->SP); + + return (pSlot < pRD->SP + m_SizeOfStackOutgoingAndScratchArea); +#else + return FALSE; +#endif +} + +void GcInfoDecoder::ReportRegisterToGC( + int regNum, + unsigned gcFlags, + PREGDISPLAY pRD, + unsigned flags, + GCEnumCallback pCallBack, + void * hCallBack) +{ + GCINFODECODER_CONTRACT; + + _ASSERTE(regNum > 0 && regNum <= 31); + + LOG((LF_GCROOTS, LL_INFO1000, "Reporting " FMT_REG, regNum )); + + OBJECTREF* pObjRef = GetRegisterSlot( regNum, pRD ); +#if defined(TARGET_UNIX) && !defined(FEATURE_NATIVEAOT) && !defined(SOS_TARGET_AMD64) + + // On PAL, we don't always have the context pointers available due to + // a limitation of an unwinding library. In such case, the context + // pointers for some nonvolatile registers are NULL. + // In such case, we let the pObjRef point to the captured register + // value in the context and pin the object itself. + if (pObjRef == NULL) + { + // Report a pinned object to GC only in the promotion phase when the + // GC is scanning roots. + GCCONTEXT* pGCCtx = (GCCONTEXT*)(hCallBack); + if (!pGCCtx->sc->promotion) + { + return; + } + + pObjRef = GetCapturedRegister(regNum, pRD); + + gcFlags |= GC_CALL_PINNED; + } +#endif // TARGET_UNIX && !SOS_TARGET_ARM64 + +#ifdef _DEBUG + if(IsScratchRegister(regNum, pRD)) + { + // Scratch registers cannot be reported for non-leaf frames + _ASSERTE(flags & ActiveStackFrame); + } + + LOG((LF_GCROOTS, LL_INFO1000, /* Part Two */ + "at" FMT_ADDR "as ", DBG_ADDR(pObjRef) )); + + VALIDATE_ROOT((gcFlags & GC_CALL_INTERIOR), hCallBack, pObjRef); + + LOG_PIPTR(pObjRef, gcFlags, hCallBack); +#endif //_DEBUG + + gcFlags |= CHECK_APP_DOMAIN; + + pCallBack(hCallBack, pObjRef, gcFlags DAC_ARG(DacSlotLocation(regNum, 0, false))); +} + #else // Unknown platform OBJECTREF* GcInfoDecoder::GetRegisterSlot( @@ -1980,6 +2118,8 @@ int GcInfoDecoder::GetStackReg(int spBase) int esp = 31; #elif defined(TARGET_LOONGARCH64) int esp = 3; +#elif defined(TARGET_RISCV64) + int esp = 2; #endif if( GC_SP_REL == spBase ) diff --git a/src/coreclr/vm/interpreter.cpp b/src/coreclr/vm/interpreter.cpp index 3eafbc0e7557e3..6938820a7c759e 100644 --- a/src/coreclr/vm/interpreter.cpp +++ b/src/coreclr/vm/interpreter.cpp @@ -91,7 +91,7 @@ InterpreterMethodInfo::InterpreterMethodInfo(CEEInfo* comp, CORINFO_METHOD_INFO* } #endif -#if defined(UNIX_AMD64_ABI) || defined(HOST_LOONGARCH64) +#if defined(UNIX_AMD64_ABI) || defined(HOST_LOONGARCH64) || defined(HOST_RISCV64) // ...or it fits into two registers. if (hasRetBuff && getClassSize(methInfo->args.retTypeClass) <= 2 * sizeof(void*)) { @@ -537,6 +537,9 @@ void Interpreter::ArgState::AddArg(unsigned canonIndex, short numSlots, bool noR #elif defined(HOST_LOONGARCH64) callerArgStackSlots += numSlots; ClrSafeInt offset(-callerArgStackSlots); +#elif defined(HOST_RISCV64) + callerArgStackSlots += numSlots; + ClrSafeInt offset(-callerArgStackSlots); #endif offset *= static_cast(sizeof(void*)); _ASSERTE(!offset.IsOverflow()); @@ -700,7 +703,17 @@ void Interpreter::ArgState::AddFPArg(unsigned canonIndex, unsigned short numSlot fpArgsUsed |= (0x1 << (numFPRegArgSlots + i)); } numFPRegArgSlots += numSlots; +#elif defined(HOST_RISCV64) + assert(numFPRegArgSlots + numSlots <= MaxNumFPRegArgSlots); + assert(!twoSlotAlign); + argIsReg[canonIndex] = ARS_FloatReg; + argOffsets[canonIndex] = numFPRegArgSlots * sizeof(void*); + for (unsigned i = 0; i < numSlots; i++) + { + fpArgsUsed |= (0x1 << (numFPRegArgSlots + i)); + } + numFPRegArgSlots += numSlots; #else #error "Unsupported architecture" #endif @@ -1125,7 +1138,7 @@ CorJitResult Interpreter::GenerateInterpreterStub(CEEInfo* comp, #elif defined(HOST_ARM) // LONGS have 2-reg alignment; inc reg if necessary. argState.AddArg(k, 2, /*noReg*/false, /*twoSlotAlign*/true); -#elif defined(HOST_AMD64) || defined(HOST_ARM64) || defined(HOST_LOONGARCH64) +#elif defined(HOST_AMD64) || defined(HOST_ARM64) || defined(HOST_LOONGARCH64) || defined(HOST_RISCV64) argState.AddArg(k); #else #error unknown platform @@ -1138,7 +1151,7 @@ CorJitResult Interpreter::GenerateInterpreterStub(CEEInfo* comp, argState.AddArg(k, 1, /*noReg*/true); #elif defined(HOST_ARM) argState.AddFPArg(k, 1, /*twoSlotAlign*/false); -#elif defined(HOST_AMD64) || defined(HOST_ARM64) || defined(HOST_LOONGARCH64) +#elif defined(HOST_AMD64) || defined(HOST_ARM64) || defined(HOST_LOONGARCH64) || defined(HOST_RISCV64) argState.AddFPArg(k, 1, false); #else #error unknown platform @@ -1151,7 +1164,7 @@ CorJitResult Interpreter::GenerateInterpreterStub(CEEInfo* comp, argState.AddArg(k, 2, /*noReg*/true); #elif defined(HOST_ARM) argState.AddFPArg(k, 2, /*twoSlotAlign*/true); -#elif defined(HOST_AMD64) || defined(HOST_ARM64) || defined(HOST_LOONGARCH64) +#elif defined(HOST_AMD64) || defined(HOST_ARM64) || defined(HOST_LOONGARCH64) || defined(HOST_RISCV64) argState.AddFPArg(k, 1, false); #else #error unknown platform @@ -1194,6 +1207,8 @@ CorJitResult Interpreter::GenerateInterpreterStub(CEEInfo* comp, } #elif defined(HOST_LOONGARCH64) argState.AddArg(k, static_cast(szSlots)); +#elif defined(HOST_RISCV64) + argState.AddArg(k, static_cast(szSlots)); #else #error unknown platform #endif @@ -1250,6 +1265,9 @@ CorJitResult Interpreter::GenerateInterpreterStub(CEEInfo* comp, // See StubLinkerCPU::EmitProlog for the layout of the stack unsigned intRegArgBaseOffset = (argState.numFPRegArgSlots) * sizeof(void*); unsigned short stackArgBaseOffset = (unsigned short) ((argState.numRegArgs + argState.numFPRegArgSlots) * sizeof(void*)); +#elif defined(HOST_RISCV64) + unsigned intRegArgBaseOffset = (argState.numFPRegArgSlots) * sizeof(void*); + unsigned short stackArgBaseOffset = (unsigned short) ((argState.numRegArgs + argState.numFPRegArgSlots) * sizeof(void*)); #else #error unsupported platform #endif @@ -1300,6 +1318,8 @@ CorJitResult Interpreter::GenerateInterpreterStub(CEEInfo* comp, argState.argOffsets[k] = (regArgsFound - 1) * sizeof(void*); #elif defined(HOST_LOONGARCH64) argState.argOffsets[k] += intRegArgBaseOffset; +#elif defined(HOST_RISCV64) + argState.argOffsets[k] += intRegArgBaseOffset; #else #error unsupported platform #endif @@ -1614,7 +1634,8 @@ CorJitResult Interpreter::GenerateInterpreterStub(CEEInfo* comp, #elif defined(HOST_LOONGARCH64) assert(!"unimplemented on LOONGARCH yet"); - +#elif defined(HOST_RISCV64) + assert(!"unimplemented on RISCV64 yet"); #else #error unsupported platform #endif @@ -6308,6 +6329,9 @@ void Interpreter::MkRefany() #elif defined(HOST_LOONGARCH64) tbr = NULL; NYI_INTERP("Unimplemented code: MkRefAny on LOONGARCH"); +#elif defined(HOST_RISCV64) + tbr = NULL; + NYI_INTERP("Unimplemented code: MkRefAny on RISCV64"); #else #error "unsupported platform" #endif @@ -9446,6 +9470,8 @@ void Interpreter::DoCallWork(bool virtualCall, void* thisArg, CORINFO_RESOLVED_T unsigned totalArgSlots = nSlots; #elif defined(HOST_LOONGARCH64) unsigned totalArgSlots = nSlots; +#elif defined(HOST_RISCV64) + unsigned totalArgSlots = nSlots; #else #error "unsupported platform" #endif diff --git a/src/coreclr/vm/interpreter.h b/src/coreclr/vm/interpreter.h index c76c4160c80cd4..51a82a6da047af 100644 --- a/src/coreclr/vm/interpreter.h +++ b/src/coreclr/vm/interpreter.h @@ -996,6 +996,8 @@ class Interpreter #endif #elif defined(HOST_LOONGARCH64) static const int MaxNumFPRegArgSlots = 8; +#elif defined(HOST_RISCV64) + static const int MaxNumFPRegArgSlots = 8; #endif ~ArgState() @@ -2056,6 +2058,8 @@ unsigned short Interpreter::NumberOfIntegerRegArgs() { return 4; } unsigned short Interpreter::NumberOfIntegerRegArgs() { return 8; } #elif defined(HOST_LOONGARCH64) unsigned short Interpreter::NumberOfIntegerRegArgs() { return 8; } +#elif defined(HOST_RISCV64) +unsigned short Interpreter::NumberOfIntegerRegArgs() { return 8; } #else #error Unsupported architecture. #endif diff --git a/src/coreclr/vm/jitinterface.cpp b/src/coreclr/vm/jitinterface.cpp index 8e888542fecfdc..767e136a21089f 100644 --- a/src/coreclr/vm/jitinterface.cpp +++ b/src/coreclr/vm/jitinterface.cpp @@ -9439,6 +9439,8 @@ uint32_t CEEInfo::getLoongArch64PassStructInRegisterFlags(CORINFO_CLASS_HANDLE c #if defined(TARGET_LOONGARCH64) size = (uint32_t)MethodTable::GetLoongArch64PassStructInRegisterFlags(cls); +#elif defined(TARGET_RISCV64) + size = (uint32_t)MethodTable::GetRiscv64PassStructInRegisterFlags(cls); #endif EE_TO_JIT_TRANSITION_LEAF(); @@ -11022,6 +11024,12 @@ void reservePersonalityRoutineSpace(uint32_t &unwindSize) // The JIT passes in a 4-byte aligned block of unwind data. _ASSERTE(IS_ALIGNED(unwindSize, sizeof(ULONG))); + // Add space for personality routine, it must be 4-byte aligned. + unwindSize += sizeof(ULONG); +#elif defined(TARGET_RISCV64) + // The JIT passes in a 4-byte aligned block of unwind data. + _ASSERTE(IS_ALIGNED(unwindSize, sizeof(ULONG))); + // Add space for personality routine, it must be 4-byte aligned. unwindSize += sizeof(ULONG); #else @@ -11246,6 +11254,14 @@ void CEEJitInfo::allocUnwindInfo ( ULONG * pPersonalityRoutineRW = (ULONG*)((BYTE *)pUnwindInfoRW + ALIGN_UP(unwindSize, sizeof(ULONG))); *pPersonalityRoutineRW = ExecutionManager::GetCLRPersonalityRoutineValue(); +#elif defined(TARGET_RISCV64) + *(LONG *)pUnwindInfoRW |= (1 << 20); // X bit + + ULONG * pPersonalityRoutineRW = (ULONG*)((BYTE *)pUnwindInfoRW + ALIGN_UP(unwindSize, sizeof(ULONG))); + *pPersonalityRoutineRW = ExecutionManager::GetCLRPersonalityRoutineValue(); + +; + #endif EE_TO_JIT_TRANSITION(); diff --git a/src/coreclr/vm/jitinterface.h b/src/coreclr/vm/jitinterface.h index cc2cccca72a096..dd9de6b0be98bb 100644 --- a/src/coreclr/vm/jitinterface.h +++ b/src/coreclr/vm/jitinterface.h @@ -401,7 +401,7 @@ extern "C" void STDCALL JIT_MemCpy(void *dest, const void *src, SIZE_T count); void STDMETHODCALLTYPE JIT_ProfilerEnterLeaveTailcallStub(UINT_PTR ProfilerHandle); -#if !defined(TARGET_ARM64) && !defined(TARGET_LOONGARCH64) +#if !defined(TARGET_ARM64) && !defined(TARGET_LOONGARCH64) && !(TARGET_RISCV64) void STDCALL JIT_StackProbe(); #endif // TARGET_ARM64 }; diff --git a/src/coreclr/vm/methodtable.cpp b/src/coreclr/vm/methodtable.cpp index c56d1fb8c7adf4..70b991bfecfb89 100644 --- a/src/coreclr/vm/methodtable.cpp +++ b/src/coreclr/vm/methodtable.cpp @@ -3473,6 +3473,616 @@ int MethodTable::GetLoongArch64PassStructInRegisterFlags(CORINFO_CLASS_HANDLE cl } #endif +#if defined(TARGET_RISCV64) + +bool MethodTable::IsRiscv64OnlyOneField(MethodTable * pMT) +{ + TypeHandle th(pMT); + + bool useNativeLayout = false; + bool ret = false; + MethodTable* pMethodTable = nullptr; + + if (!th.IsTypeDesc()) + { + pMethodTable = th.AsMethodTable(); + if (pMethodTable->HasLayout()) + { + useNativeLayout = true; + } + else if (th.GetSize() <= 16 /*MAX_PASS_MULTIREG_BYTES*/) + { + DWORD numIntroducedFields = pMethodTable->GetNumIntroducedInstanceFields(); + + if (numIntroducedFields == 1) + { + FieldDesc *pFieldStart = pMethodTable->GetApproxFieldDescListRaw(); + + CorElementType fieldType = pFieldStart[0].GetFieldType(); + + if (CorTypeInfo::IsPrimitiveType_NoThrow(fieldType)) + { + ret = true; + } + else if (fieldType == ELEMENT_TYPE_VALUETYPE) + { + pMethodTable = pFieldStart->GetApproxFieldTypeHandleThrowing().GetMethodTable(); + if (pMethodTable->GetNumIntroducedInstanceFields() == 1) + { + ret = IsRiscv64OnlyOneField(pMethodTable); + } + } + } + goto _End_arg; + } + } + else + { + _ASSERTE(th.IsNativeValueType()); + + useNativeLayout = true; + pMethodTable = th.AsNativeValueType(); + } + _ASSERTE(pMethodTable != nullptr); + + if (useNativeLayout) + { + if (th.GetSize() <= 16 /*MAX_PASS_MULTIREG_BYTES*/) + { + DWORD numIntroducedFields = pMethodTable->GetNativeLayoutInfo()->GetNumFields(); + FieldDesc *pFieldStart = nullptr; + + if (numIntroducedFields == 1) + { + pFieldStart = pMethodTable->GetApproxFieldDescListRaw(); + + CorElementType fieldType = pFieldStart->GetFieldType(); + + bool isFixedBuffer = (CorTypeInfo::IsPrimitiveType_NoThrow(fieldType) + || fieldType == ELEMENT_TYPE_VALUETYPE) + && (pFieldStart->GetOffset() == 0) + && pMethodTable->HasLayout() + && (pMethodTable->GetNumInstanceFieldBytes() % pFieldStart->GetSize() == 0); + + if (isFixedBuffer) + { + numIntroducedFields = pMethodTable->GetNumInstanceFieldBytes() / pFieldStart->GetSize(); + if (numIntroducedFields != 1) + { + goto _End_arg; + } + } + + if (CorTypeInfo::IsPrimitiveType_NoThrow(fieldType)) + { + ret = true; + } + else if (fieldType == ELEMENT_TYPE_VALUETYPE) + { + const NativeFieldDescriptor *pNativeFieldDescs = pMethodTable->GetNativeLayoutInfo()->GetNativeFieldDescriptors(); + NativeFieldCategory nfc = pNativeFieldDescs->GetCategory(); + if (nfc == NativeFieldCategory::NESTED) + { + pMethodTable = pNativeFieldDescs->GetNestedNativeMethodTable(); + ret = IsRiscv64OnlyOneField(pMethodTable); + } + else if (nfc != NativeFieldCategory::ILLEGAL) + { + ret = true; + } + } + } + else + { + ret = false; + } + } + } +_End_arg: + + return ret; +} + +int MethodTable::GetRiscv64PassStructInRegisterFlags(CORINFO_CLASS_HANDLE cls) +{ + TypeHandle th(cls); + + bool useNativeLayout = false; + int size = STRUCT_NO_FLOAT_FIELD; + MethodTable* pMethodTable = nullptr; + + if (!th.IsTypeDesc()) + { + pMethodTable = th.AsMethodTable(); + if (pMethodTable->HasLayout()) + { + useNativeLayout = true; + } + else if (th.GetSize() <= 16 /*MAX_PASS_MULTIREG_BYTES*/) + { + DWORD numIntroducedFields = pMethodTable->GetNumIntroducedInstanceFields(); + + if (numIntroducedFields == 1) + { + FieldDesc *pFieldStart = pMethodTable->GetApproxFieldDescListRaw(); + + CorElementType fieldType = pFieldStart[0].GetFieldType(); + + if (CorTypeInfo::IsPrimitiveType_NoThrow(fieldType)) + { + if (fieldType == ELEMENT_TYPE_R4) + { + size = STRUCT_FLOAT_FIELD_ONLY_ONE; + } + else if (fieldType == ELEMENT_TYPE_R8) + { + size = STRUCT_FLOAT_FIELD_ONLY_ONE | STRUCT_FIRST_FIELD_SIZE_IS8; + } + } + else if (fieldType == ELEMENT_TYPE_VALUETYPE) + { + pMethodTable = pFieldStart->GetApproxFieldTypeHandleThrowing().GetMethodTable(); + size = GetRiscv64PassStructInRegisterFlags((CORINFO_CLASS_HANDLE)pMethodTable); + } + } + else if (numIntroducedFields == 2) + { + FieldDesc *pFieldSecond; + FieldDesc *pFieldFirst = pMethodTable->GetApproxFieldDescListRaw(); + if (pFieldFirst->GetOffset() == 0) + { + pFieldSecond = pFieldFirst + 1; + } + else + { + pFieldSecond = pFieldFirst; + pFieldFirst = pFieldFirst + 1; + } + assert(pFieldFirst->GetOffset() == 0); + + if (pFieldFirst->GetSize() > 8) + { + goto _End_arg; + } + + CorElementType fieldType = pFieldFirst[0].GetFieldType(); + if (CorTypeInfo::IsPrimitiveType_NoThrow(fieldType)) + { + if (fieldType == ELEMENT_TYPE_R4) + { + size = STRUCT_FLOAT_FIELD_FIRST; + } + else if (fieldType == ELEMENT_TYPE_R8) + { + size = STRUCT_FIRST_FIELD_DOUBLE; + } + else if (pFieldFirst[0].GetSize() == 8) + { + size = STRUCT_FIRST_FIELD_SIZE_IS8; + } + + } + else if (fieldType == ELEMENT_TYPE_VALUETYPE) + { + pMethodTable = pFieldFirst->GetApproxFieldTypeHandleThrowing().GetMethodTable(); + if (IsRiscv64OnlyOneField(pMethodTable)) + { + size = GetRiscv64PassStructInRegisterFlags((CORINFO_CLASS_HANDLE)pMethodTable); + if ((size & STRUCT_FLOAT_FIELD_ONLY_ONE) != 0) + { + size = pFieldFirst[0].GetSize() == 8 ? STRUCT_FIRST_FIELD_DOUBLE : STRUCT_FLOAT_FIELD_FIRST; + } + else if (size == STRUCT_NO_FLOAT_FIELD) + { + size = pFieldFirst[0].GetSize() == 8 ? STRUCT_FIRST_FIELD_SIZE_IS8: 0; + } + else + { + size = STRUCT_NO_FLOAT_FIELD; + goto _End_arg; + } + } + else + { + size = STRUCT_NO_FLOAT_FIELD; + goto _End_arg; + } + } + else if (pFieldFirst[0].GetSize() == 8) + { + size = STRUCT_FIRST_FIELD_SIZE_IS8; + } + + fieldType = pFieldSecond[0].GetFieldType(); + if (pFieldSecond[0].GetSize() > 8) + { + size = STRUCT_NO_FLOAT_FIELD; + goto _End_arg; + } + else if (CorTypeInfo::IsPrimitiveType_NoThrow(fieldType)) + { + if (fieldType == ELEMENT_TYPE_R4) + { + size = size & STRUCT_FLOAT_FIELD_FIRST ? (size ^ STRUCT_MERGE_FIRST_SECOND) : (size | STRUCT_FLOAT_FIELD_SECOND); + } + else if (fieldType == ELEMENT_TYPE_R8) + { + size = size & STRUCT_FLOAT_FIELD_FIRST ? (size ^ STRUCT_MERGE_FIRST_SECOND_8) : (size | STRUCT_SECOND_FIELD_DOUBLE); + } + else if ((size & STRUCT_FLOAT_FIELD_FIRST) == 0) + { + size = STRUCT_NO_FLOAT_FIELD; + } + else if (pFieldSecond[0].GetSize() == 8) + { + size |= STRUCT_SECOND_FIELD_SIZE_IS8; + } + } + else if (fieldType == ELEMENT_TYPE_VALUETYPE) + { + pMethodTable = pFieldSecond[0].GetApproxFieldTypeHandleThrowing().GetMethodTable(); + if (IsRiscv64OnlyOneField(pMethodTable)) + { + int size2 = GetRiscv64PassStructInRegisterFlags((CORINFO_CLASS_HANDLE)pMethodTable); + if ((size2 & STRUCT_FLOAT_FIELD_ONLY_ONE) != 0) + { + if (pFieldSecond[0].GetSize() == 8) + { + size = size & STRUCT_FLOAT_FIELD_FIRST ? (size ^ STRUCT_MERGE_FIRST_SECOND_8) : (size | STRUCT_SECOND_FIELD_DOUBLE); + } + else + { + size = size & STRUCT_FLOAT_FIELD_FIRST ? (size ^ STRUCT_MERGE_FIRST_SECOND) : (size | STRUCT_FLOAT_FIELD_SECOND); + } + } + else if ((size & STRUCT_FLOAT_FIELD_FIRST) == 0) + { + size = STRUCT_NO_FLOAT_FIELD; + } + else if (size2 == STRUCT_NO_FLOAT_FIELD) + { + size |= pFieldSecond[0].GetSize() == 8 ? STRUCT_SECOND_FIELD_SIZE_IS8 : 0; + } + else + { + size = STRUCT_NO_FLOAT_FIELD; + } + } + else + { + size = STRUCT_NO_FLOAT_FIELD; + } + } + else if ((size & STRUCT_FLOAT_FIELD_FIRST) == 0) + { + size = STRUCT_NO_FLOAT_FIELD; + } + else if (pFieldSecond[0].GetSize() == 8) + { + size |= STRUCT_SECOND_FIELD_SIZE_IS8; + } + } + + goto _End_arg; + } + } + else + { + _ASSERTE(th.IsNativeValueType()); + + useNativeLayout = true; + pMethodTable = th.AsNativeValueType(); + } + _ASSERTE(pMethodTable != nullptr); + + if (useNativeLayout) + { + if (th.GetSize() <= 16 /*MAX_PASS_MULTIREG_BYTES*/) + { + DWORD numIntroducedFields = pMethodTable->GetNativeLayoutInfo()->GetNumFields(); + FieldDesc *pFieldStart = nullptr; + + if (numIntroducedFields == 1) + { + pFieldStart = pMethodTable->GetApproxFieldDescListRaw(); + + CorElementType fieldType = pFieldStart->GetFieldType(); + + bool isFixedBuffer = (CorTypeInfo::IsPrimitiveType_NoThrow(fieldType) + || fieldType == ELEMENT_TYPE_VALUETYPE) + && (pFieldStart->GetOffset() == 0) + && pMethodTable->HasLayout() + && (pMethodTable->GetNumInstanceFieldBytes() % pFieldStart->GetSize() == 0); + + if (isFixedBuffer) + { + numIntroducedFields = pMethodTable->GetNumInstanceFieldBytes() / pFieldStart->GetSize(); + if (numIntroducedFields > 2) + { + goto _End_arg; + } + + if (fieldType == ELEMENT_TYPE_R4) + { + if (numIntroducedFields == 1) + { + size = STRUCT_FLOAT_FIELD_ONLY_ONE; + } + else if (numIntroducedFields == 2) + { + size = STRUCT_FLOAT_FIELD_ONLY_TWO; + } + goto _End_arg; + } + else if (fieldType == ELEMENT_TYPE_R8) + { + if (numIntroducedFields == 1) + { + size = STRUCT_FLOAT_FIELD_ONLY_ONE | STRUCT_FIRST_FIELD_SIZE_IS8; + } + else if (numIntroducedFields == 2) + { + size = STRUCT_FIELD_TWO_DOUBLES; + } + goto _End_arg; + } + } + + if (CorTypeInfo::IsPrimitiveType_NoThrow(fieldType)) + { + if (fieldType == ELEMENT_TYPE_R4) + { + size = STRUCT_FLOAT_FIELD_ONLY_ONE; + } + else if (fieldType == ELEMENT_TYPE_R8) + { + size = STRUCT_FLOAT_FIELD_ONLY_ONE | STRUCT_FIRST_FIELD_SIZE_IS8; + } + } + else if (fieldType == ELEMENT_TYPE_VALUETYPE) + { + const NativeFieldDescriptor *pNativeFieldDescs = pMethodTable->GetNativeLayoutInfo()->GetNativeFieldDescriptors(); + NativeFieldCategory nfc = pNativeFieldDescs->GetCategory(); + if (nfc == NativeFieldCategory::NESTED) + { + pMethodTable = pNativeFieldDescs->GetNestedNativeMethodTable(); + size = GetRiscv64PassStructInRegisterFlags((CORINFO_CLASS_HANDLE)pMethodTable); + return size; + } + else if (nfc == NativeFieldCategory::FLOAT) + { + if (pFieldStart->GetSize() == 4) + { + size = STRUCT_FLOAT_FIELD_ONLY_ONE; + } + else if (pFieldStart->GetSize() == 8) + { + size = STRUCT_FLOAT_FIELD_ONLY_ONE | STRUCT_FIRST_FIELD_SIZE_IS8; + } + } + } + } + else if (numIntroducedFields == 2) + { + pFieldStart = pMethodTable->GetApproxFieldDescListRaw(); + + if (pFieldStart->GetSize() > 8) + { + goto _End_arg; + } + + if (pFieldStart->GetOffset() || !pFieldStart[1].GetOffset() || (pFieldStart[0].GetSize() > pFieldStart[1].GetOffset())) + { + goto _End_arg; + } + + CorElementType fieldType = pFieldStart[0].GetFieldType(); + if (CorTypeInfo::IsPrimitiveType_NoThrow(fieldType)) + { + if (fieldType == ELEMENT_TYPE_R4) + { + size = STRUCT_FLOAT_FIELD_FIRST; + } + else if (fieldType == ELEMENT_TYPE_R8) + { + size = STRUCT_FIRST_FIELD_DOUBLE; + } + else if (pFieldStart[0].GetSize() == 8) + { + size = STRUCT_FIRST_FIELD_SIZE_IS8; + } + + fieldType = pFieldStart[1].GetFieldType(); + if (CorTypeInfo::IsPrimitiveType_NoThrow(fieldType)) + { + if (fieldType == ELEMENT_TYPE_R4) + { + size = size & STRUCT_FLOAT_FIELD_FIRST ? (size ^ STRUCT_MERGE_FIRST_SECOND) : (size | STRUCT_FLOAT_FIELD_SECOND); + } + else if (fieldType == ELEMENT_TYPE_R8) + { + size = size & STRUCT_FLOAT_FIELD_FIRST ? (size ^ STRUCT_MERGE_FIRST_SECOND_8) : (size | STRUCT_SECOND_FIELD_DOUBLE); + } + else if ((size & STRUCT_FLOAT_FIELD_FIRST) == 0) + { + size = STRUCT_NO_FLOAT_FIELD; + } + else if (pFieldStart[1].GetSize() == 8) + { + size |= STRUCT_SECOND_FIELD_SIZE_IS8; + } + goto _End_arg; + } + } + else if (fieldType == ELEMENT_TYPE_VALUETYPE) + { + const NativeFieldDescriptor *pNativeFieldDescs = pMethodTable->GetNativeLayoutInfo()->GetNativeFieldDescriptors(); + + NativeFieldCategory nfc = pNativeFieldDescs->GetCategory(); + + if (nfc == NativeFieldCategory::NESTED) + { + if (pNativeFieldDescs->GetNumElements() != 1) + { + size = STRUCT_NO_FLOAT_FIELD; + goto _End_arg; + } + + MethodTable* pMethodTable2 = pNativeFieldDescs->GetNestedNativeMethodTable(); + + if (!IsRiscv64OnlyOneField(pMethodTable2)) + { + size = STRUCT_NO_FLOAT_FIELD; + goto _End_arg; + } + + size = GetRiscv64PassStructInRegisterFlags((CORINFO_CLASS_HANDLE)pMethodTable2); + if ((size & STRUCT_FLOAT_FIELD_ONLY_ONE) != 0) + { + if (pFieldStart->GetSize() == 8) + { + size = STRUCT_FIRST_FIELD_DOUBLE; + } + else + { + size = STRUCT_FLOAT_FIELD_FIRST; + } + } + else if (pFieldStart->GetSize() == 8) + { + size = STRUCT_FIRST_FIELD_SIZE_IS8; + } + else + { + size = STRUCT_NO_FLOAT_FIELD; + goto _End_arg; + } + } + else if (nfc == NativeFieldCategory::FLOAT) + { + if (pFieldStart[0].GetSize() == 4) + { + size = STRUCT_FLOAT_FIELD_FIRST; + } + else if (pFieldStart[0].GetSize() == 8) + { + _ASSERTE(pMethodTable->GetNativeSize() == 8); + size = STRUCT_FIRST_FIELD_DOUBLE; + } + } + else if (pFieldStart[0].GetSize() == 8) + { + size = STRUCT_FIRST_FIELD_SIZE_IS8; + } + } + else if (pFieldStart[0].GetSize() == 8) + { + size = STRUCT_FIRST_FIELD_SIZE_IS8; + } + + fieldType = pFieldStart[1].GetFieldType(); + if (pFieldStart[1].GetSize() > 8) + { + size = STRUCT_NO_FLOAT_FIELD; + goto _End_arg; + } + else if (CorTypeInfo::IsPrimitiveType_NoThrow(fieldType)) + { + if (fieldType == ELEMENT_TYPE_R4) + { + size = size & STRUCT_FLOAT_FIELD_FIRST ? (size ^ STRUCT_MERGE_FIRST_SECOND) : (size | STRUCT_FLOAT_FIELD_SECOND); + } + else if (fieldType == ELEMENT_TYPE_R8) + { + size = size & STRUCT_FLOAT_FIELD_FIRST ? (size ^ STRUCT_MERGE_FIRST_SECOND_8) : (size | STRUCT_SECOND_FIELD_DOUBLE); + } + else if ((size & STRUCT_FLOAT_FIELD_FIRST) == 0) + { + size = STRUCT_NO_FLOAT_FIELD; + } + else if (pFieldStart[1].GetSize() == 8) + { + size |= STRUCT_SECOND_FIELD_SIZE_IS8; + } + } + else if (fieldType == ELEMENT_TYPE_VALUETYPE) + { + const NativeFieldDescriptor *pNativeFieldDescs = pMethodTable->GetNativeLayoutInfo()->GetNativeFieldDescriptors(); + NativeFieldCategory nfc = pNativeFieldDescs[1].GetCategory(); + + if (nfc == NativeFieldCategory::NESTED) + { + if (pNativeFieldDescs[1].GetNumElements() != 1) + { + size = STRUCT_NO_FLOAT_FIELD; + goto _End_arg; + } + + MethodTable* pMethodTable2 = pNativeFieldDescs[1].GetNestedNativeMethodTable(); + + if (!IsRiscv64OnlyOneField(pMethodTable2)) + { + size = STRUCT_NO_FLOAT_FIELD; + goto _End_arg; + } + + if ((GetRiscv64PassStructInRegisterFlags((CORINFO_CLASS_HANDLE)pMethodTable2) & STRUCT_FLOAT_FIELD_ONLY_ONE) != 0) + { + if (pFieldStart[1].GetSize() == 4) + { + size = size & STRUCT_FLOAT_FIELD_FIRST ? (size ^ STRUCT_MERGE_FIRST_SECOND) : (size | STRUCT_FLOAT_FIELD_SECOND); + } + else if (pFieldStart[1].GetSize() == 8) + { + size = size & STRUCT_FLOAT_FIELD_FIRST ? (size ^ STRUCT_MERGE_FIRST_SECOND_8) : (size | STRUCT_SECOND_FIELD_DOUBLE); + } + } + else if ((size & STRUCT_FLOAT_FIELD_FIRST) == 0) + { + size = STRUCT_NO_FLOAT_FIELD; + } + else if (pFieldStart[1].GetSize() == 8) + { + size |= STRUCT_SECOND_FIELD_SIZE_IS8; + } + } + else if (nfc == NativeFieldCategory::FLOAT) + { + if (pFieldStart[1].GetSize() == 4) + { + size = size & STRUCT_FLOAT_FIELD_FIRST ? (size ^ STRUCT_MERGE_FIRST_SECOND) : (size | STRUCT_FLOAT_FIELD_SECOND); + } + else if (pFieldStart[1].GetSize() == 8) + { + size = size & STRUCT_FLOAT_FIELD_FIRST ? (size ^ STRUCT_MERGE_FIRST_SECOND_8) : (size | STRUCT_SECOND_FIELD_DOUBLE); + } + } + else if ((size & STRUCT_FLOAT_FIELD_FIRST) == 0) + { + size = STRUCT_NO_FLOAT_FIELD; + } + else if (pFieldStart[1].GetSize() == 8) + { + size |= STRUCT_SECOND_FIELD_SIZE_IS8; + } + } + else if ((size & STRUCT_FLOAT_FIELD_FIRST) == 0) + { + size = STRUCT_NO_FLOAT_FIELD; + } + else if (pFieldStart[1].GetSize() == 8) + { + size |= STRUCT_SECOND_FIELD_SIZE_IS8; + } + } + } + } +_End_arg: + + return size; +} +#endif + #if !defined(DACCESS_COMPILE) //========================================================================================== void MethodTable::AllocateRegularStaticBoxes() diff --git a/src/coreclr/vm/methodtable.h b/src/coreclr/vm/methodtable.h index 2a12d9aee125c2..c5ffcc68f71082 100644 --- a/src/coreclr/vm/methodtable.h +++ b/src/coreclr/vm/methodtable.h @@ -752,6 +752,11 @@ class MethodTable static int GetLoongArch64PassStructInRegisterFlags(CORINFO_CLASS_HANDLE clh); #endif +#if defined(TARGET_RISCV64) + static bool IsRiscv64OnlyOneField(MethodTable * pMT); + static int GetRiscv64PassStructInRegisterFlags(CORINFO_CLASS_HANDLE clh); +#endif + #if defined(UNIX_AMD64_ABI_ITF) // Builds the internal data structures and classifies struct eightbytes for Amd System V calling convention. bool ClassifyEightBytes(SystemVStructRegisterPassingHelperPtr helperPtr, unsigned int nestingLevel, unsigned int startOffsetOfStruct, bool isNativeStruct); diff --git a/src/coreclr/vm/precode.cpp b/src/coreclr/vm/precode.cpp index c997af5f678818..6049a39d0e3c5f 100644 --- a/src/coreclr/vm/precode.cpp +++ b/src/coreclr/vm/precode.cpp @@ -610,6 +610,8 @@ void StubPrecode::StaticInitialize() #endif #ifdef TARGET_LOONGARCH64 _ASSERTE(((*((short*)PCODEToPINSTR((PCODE)StubPrecodeCode) + OFFSETOF_PRECODE_TYPE)) >> 5) == StubPrecode::Type); +#elif TARGET_RISCV64 + _ASSERTE((*((BYTE*)PCODEToPINSTR((PCODE)StubPrecodeCode) + OFFSETOF_PRECODE_TYPE)) == StubPrecode::Type); #else _ASSERTE((*((BYTE*)PCODEToPINSTR((PCODE)StubPrecodeCode) + OFFSETOF_PRECODE_TYPE)) == StubPrecode::Type); #endif @@ -722,6 +724,8 @@ void FixupPrecode::StaticInitialize() #endif #ifdef TARGET_LOONGARCH64 _ASSERTE(((*((short*)PCODEToPINSTR((PCODE)StubPrecodeCode) + OFFSETOF_PRECODE_TYPE)) >> 5) == StubPrecode::Type); +#elif TARGET_RISCV64 + _ASSERTE((*((BYTE*)PCODEToPINSTR((PCODE)FixupPrecodeCode) + OFFSETOF_PRECODE_TYPE)) == FixupPrecode::Type); #else _ASSERTE(*((BYTE*)PCODEToPINSTR((PCODE)FixupPrecodeCode) + OFFSETOF_PRECODE_TYPE) == FixupPrecode::Type); #endif diff --git a/src/coreclr/vm/precode.h b/src/coreclr/vm/precode.h index 4822ccfec7054d..7862caccfd9f27 100644 --- a/src/coreclr/vm/precode.h +++ b/src/coreclr/vm/precode.h @@ -44,6 +44,11 @@ EXTERN_C VOID STDCALL PrecodeRemotingThunk(); #define SIZEOF_PRECODE_BASE CODE_SIZE_ALIGN #define OFFSETOF_PRECODE_TYPE 0 +#elif defined(HOST_RISCV64) + +#define SIZEOF_PRECODE_BASE CODE_SIZE_ALIGN +#define OFFSETOF_PRECODE_TYPE 0 + #endif // HOST_AMD64 #ifndef DACCESS_COMPILE @@ -63,6 +68,8 @@ struct InvalidPrecode static const int Type = 0; #elif defined(HOST_LOONGARCH64) static const int Type = 0xff; +#elif defined(HOST_RISCV64) + static const int Type = 0xff; #endif }; @@ -98,6 +105,9 @@ struct StubPrecode #elif defined(HOST_LOONGARCH64) static const int Type = 0x4; static const int CodeSize = 24; +#elif defined(HOST_RISCV64) + static const int Type = 0x17; + static const int CodeSize = 24; #endif // HOST_AMD64 BYTE m_code[CodeSize]; @@ -234,6 +244,10 @@ struct FixupPrecode static const int Type = 0x3; static const int CodeSize = 32; static const int FixupCodeOffset = 12; +#elif defined(HOST_RISCV64) + static const int Type = 0x97; + static const int CodeSize = 32; + static const int FixupCodeOffset = 10; #endif // HOST_AMD64 BYTE m_code[CodeSize]; @@ -422,10 +436,13 @@ class Precode { #ifdef OFFSETOF_PRECODE_TYPE -#ifdef TARGET_LOONGARCH64 +#if defined(TARGET_LOONGARCH64) assert(0 == OFFSETOF_PRECODE_TYPE); short type = *((short*)m_data); type >>= 5; +#elif defined(TARGET_RISCV64) + assert(0 == OFFSETOF_PRECODE_TYPE); + BYTE type = *((BYTE*)m_data + OFFSETOF_PRECODE_TYPE); #else BYTE type = m_data[OFFSETOF_PRECODE_TYPE]; #endif diff --git a/src/coreclr/vm/prestub.cpp b/src/coreclr/vm/prestub.cpp index c8a95d77a18cd9..c05b76095897ce 100644 --- a/src/coreclr/vm/prestub.cpp +++ b/src/coreclr/vm/prestub.cpp @@ -2303,7 +2303,7 @@ PCODE TheVarargNDirectStub(BOOL hasRetBuffArg) { LIMITED_METHOD_CONTRACT; -#if !defined(TARGET_X86) && !defined(TARGET_ARM64) && !defined(TARGET_LOONGARCH64) +#if !defined(TARGET_X86) && !defined(TARGET_ARM64) && !defined(TARGET_LOONGARCH64) && !defined(TARGET_RISCV64) if (hasRetBuffArg) { return GetEEFuncEntryPoint(VarargPInvokeStub_RetBuffArg); diff --git a/src/coreclr/vm/riscv64/asmconstants.h b/src/coreclr/vm/riscv64/asmconstants.h index 9a0cdd4e406d37..12aedda2db5f87 100644 --- a/src/coreclr/vm/riscv64/asmconstants.h +++ b/src/coreclr/vm/riscv64/asmconstants.h @@ -1,4 +1,253 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +// asmconstants.h - +// +// This header defines field offsets and constants used by assembly code +// Be sure to rebuild clr/src/vm/ceemain.cpp after changing this file, to +// ensure that the constants match the expected C/C++ values -#error "TODO-RISCV64: missing implementation" +#include "../../inc/switches.h" + +//----------------------------------------------------------------------------- + +#ifndef ASMCONSTANTS_C_ASSERT +#define ASMCONSTANTS_C_ASSERT(cond) +#endif + +#ifndef ASMCONSTANTS_RUNTIME_ASSERT +#define ASMCONSTANTS_RUNTIME_ASSERT(cond) +#endif + +// Some constants are different in _DEBUG builds. This macro factors out ifdefs from below. +#ifdef _DEBUG +#define DBG_FRE(dbg,fre) dbg +#else +#define DBG_FRE(dbg,fre) fre +#endif + +#define DynamicHelperFrameFlags_Default 0 +#define DynamicHelperFrameFlags_ObjectArg 1 +#define DynamicHelperFrameFlags_ObjectArg2 2 + +#define Thread__m_fPreemptiveGCDisabled 0x0C +#define Thread__m_pFrame 0x10 + +ASMCONSTANTS_C_ASSERT(Thread__m_fPreemptiveGCDisabled == offsetof(Thread, m_fPreemptiveGCDisabled)); +ASMCONSTANTS_C_ASSERT(Thread__m_pFrame == offsetof(Thread, m_pFrame)); + +#define Thread_m_pFrame Thread__m_pFrame +#define Thread_m_fPreemptiveGCDisabled Thread__m_fPreemptiveGCDisabled + +#define METHODDESC_REGISTER t2 + +#define SIZEOF__ArgumentRegisters 0x40 +ASMCONSTANTS_C_ASSERT(SIZEOF__ArgumentRegisters == sizeof(ArgumentRegisters)) + +// 8*8=0x40, fa0-fa7 +#define SIZEOF__FloatArgumentRegisters 0x40 +ASMCONSTANTS_C_ASSERT(SIZEOF__FloatArgumentRegisters == sizeof(FloatArgumentRegisters)) + +#define ASM_ENREGISTERED_RETURNTYPE_MAXSIZE 0x10 +ASMCONSTANTS_C_ASSERT(ASM_ENREGISTERED_RETURNTYPE_MAXSIZE == ENREGISTERED_RETURNTYPE_MAXSIZE) + +#define CallDescrData__pSrc 0x00 +#define CallDescrData__numStackSlots 0x08 +#define CallDescrData__pArgumentRegisters 0x10 +#define CallDescrData__pFloatArgumentRegisters 0x18 +#define CallDescrData__fpReturnSize 0x20 +#define CallDescrData__pTarget 0x28 +#define CallDescrData__returnValue 0x30 + +ASMCONSTANTS_C_ASSERT(CallDescrData__pSrc == offsetof(CallDescrData, pSrc)) +ASMCONSTANTS_C_ASSERT(CallDescrData__numStackSlots == offsetof(CallDescrData, numStackSlots)) +ASMCONSTANTS_C_ASSERT(CallDescrData__pArgumentRegisters == offsetof(CallDescrData, pArgumentRegisters)) +ASMCONSTANTS_C_ASSERT(CallDescrData__pFloatArgumentRegisters == offsetof(CallDescrData, pFloatArgumentRegisters)) +ASMCONSTANTS_C_ASSERT(CallDescrData__fpReturnSize == offsetof(CallDescrData, fpReturnSize)) +ASMCONSTANTS_C_ASSERT(CallDescrData__pTarget == offsetof(CallDescrData, pTarget)) +ASMCONSTANTS_C_ASSERT(CallDescrData__returnValue == offsetof(CallDescrData, returnValue)) + +#define CallDescrData__flagOneFloat 0x1 +#define CallDescrData__flagOneDouble 0x11 +#define CallDescrData__flagFloatInt 0x2 +#define CallDescrData__flagFloatLong 0x22 +#define CallDescrData__flagDoubleInt 0x12 +#define CallDescrData__flagDoubleLong 0x32 +#define CallDescrData__flagIntFloat 0x4 +#define CallDescrData__flagIntDouble 0x24 +#define CallDescrData__flagLongFloat 0x14 +#define CallDescrData__flagLongDouble 0x34 +#define CallDescrData__flagFloatFloat 0x8 +#define CallDescrData__flagFloatDouble 0x28 +#define CallDescrData__flagDoubleFloat 0x18 +#define CallDescrData__flagDoubleDouble 0x38 + +ASMCONSTANTS_C_ASSERT(CallDescrData__flagOneFloat == (int)STRUCT_FLOAT_FIELD_ONLY_ONE) +ASMCONSTANTS_C_ASSERT(CallDescrData__flagOneDouble == (int)(STRUCT_FLOAT_FIELD_ONLY_ONE | STRUCT_FIRST_FIELD_SIZE_IS8)) +ASMCONSTANTS_C_ASSERT(CallDescrData__flagFloatInt == (int)STRUCT_FLOAT_FIELD_FIRST) +ASMCONSTANTS_C_ASSERT(CallDescrData__flagFloatLong == (int)(STRUCT_FLOAT_FIELD_FIRST | STRUCT_SECOND_FIELD_SIZE_IS8)) +ASMCONSTANTS_C_ASSERT(CallDescrData__flagDoubleInt == (int)(STRUCT_FLOAT_FIELD_FIRST | STRUCT_FIRST_FIELD_SIZE_IS8)) +ASMCONSTANTS_C_ASSERT(CallDescrData__flagDoubleLong == (int)(CallDescrData__flagDoubleInt | STRUCT_SECOND_FIELD_SIZE_IS8)) +ASMCONSTANTS_C_ASSERT(CallDescrData__flagIntFloat == (int)STRUCT_FLOAT_FIELD_SECOND) +ASMCONSTANTS_C_ASSERT(CallDescrData__flagIntDouble == (int)(STRUCT_FLOAT_FIELD_SECOND | STRUCT_SECOND_FIELD_SIZE_IS8)) +ASMCONSTANTS_C_ASSERT(CallDescrData__flagLongFloat == (int)(STRUCT_FLOAT_FIELD_SECOND | STRUCT_FIRST_FIELD_SIZE_IS8)) +ASMCONSTANTS_C_ASSERT(CallDescrData__flagLongDouble == (int)(CallDescrData__flagLongFloat | STRUCT_SECOND_FIELD_SIZE_IS8)) +ASMCONSTANTS_C_ASSERT(CallDescrData__flagFloatFloat == (int)STRUCT_FLOAT_FIELD_ONLY_TWO) +ASMCONSTANTS_C_ASSERT(CallDescrData__flagFloatDouble == (int)(STRUCT_FLOAT_FIELD_ONLY_TWO | STRUCT_SECOND_FIELD_SIZE_IS8)) +ASMCONSTANTS_C_ASSERT(CallDescrData__flagDoubleFloat == (int)(STRUCT_FLOAT_FIELD_ONLY_TWO | STRUCT_FIRST_FIELD_SIZE_IS8)) +ASMCONSTANTS_C_ASSERT(CallDescrData__flagDoubleDouble == (int)(CallDescrData__flagDoubleFloat | STRUCT_SECOND_FIELD_SIZE_IS8)) + +#define CORINFO_NullReferenceException_ASM 0 +ASMCONSTANTS_C_ASSERT( CORINFO_NullReferenceException_ASM + == CORINFO_NullReferenceException); + + +#define CORINFO_IndexOutOfRangeException_ASM 3 +ASMCONSTANTS_C_ASSERT( CORINFO_IndexOutOfRangeException_ASM + == CORINFO_IndexOutOfRangeException); + + +// Offset of the array containing the address of captured registers in MachState +#define MachState__captureCalleeSavedRegisters 0x0 +ASMCONSTANTS_C_ASSERT(MachState__captureCalleeSavedRegisters == offsetof(MachState, captureCalleeSavedRegisters)) + +// Offset of the array containing the address of preserved registers in MachState +#define MachState__ptrCalleeSavedRegisters 0x70 +ASMCONSTANTS_C_ASSERT(MachState__ptrCalleeSavedRegisters == offsetof(MachState, ptrCalleeSavedRegisters)) + +#define MachState__isValid 0xf0 +ASMCONSTANTS_C_ASSERT(MachState__isValid == offsetof(MachState, _isValid)) + +#define LazyMachState_captureCalleeSavedRegisters MachState__captureCalleeSavedRegisters +ASMCONSTANTS_C_ASSERT(LazyMachState_captureCalleeSavedRegisters == offsetof(LazyMachState, captureCalleeSavedRegisters)) + +#define LazyMachState_captureSp (MachState__isValid+8) // padding for alignment +ASMCONSTANTS_C_ASSERT(LazyMachState_captureSp == offsetof(LazyMachState, captureSp)) + +#define LazyMachState_captureIp (LazyMachState_captureSp+8) +ASMCONSTANTS_C_ASSERT(LazyMachState_captureIp == offsetof(LazyMachState, captureIp)) + +#define VASigCookie__pNDirectILStub 0x8 +ASMCONSTANTS_C_ASSERT(VASigCookie__pNDirectILStub == offsetof(VASigCookie, pNDirectILStub)) + +#define DelegateObject___methodPtr 0x18 +ASMCONSTANTS_C_ASSERT(DelegateObject___methodPtr == offsetof(DelegateObject, _methodPtr)); + +#define DelegateObject___target 0x08 +ASMCONSTANTS_C_ASSERT(DelegateObject___target == offsetof(DelegateObject, _target)); + +#define SIZEOF__GSCookie 0x8 +ASMCONSTANTS_C_ASSERT(SIZEOF__GSCookie == sizeof(GSCookie)); + +#define SIZEOF__Frame 0x10 +ASMCONSTANTS_C_ASSERT(SIZEOF__Frame == sizeof(Frame)); + +#define SIZEOF__CONTEXT 0x220 +ASMCONSTANTS_C_ASSERT(SIZEOF__CONTEXT == sizeof(T_CONTEXT)); + + +//========================================= +#define MethodTable__m_dwFlags 0x0 +ASMCONSTANTS_C_ASSERT(MethodTable__m_dwFlags == offsetof(MethodTable, m_dwFlags)); + +#define MethodTable__m_BaseSize 0x04 +ASMCONSTANTS_C_ASSERT(MethodTable__m_BaseSize == offsetof(MethodTable, m_BaseSize)); + +#define MethodTable__m_ElementType DBG_FRE(0x38, 0x30) +ASMCONSTANTS_C_ASSERT(MethodTable__m_ElementType == offsetof(MethodTable, m_pMultipurposeSlot1)); + +#define ArrayBase__m_NumComponents 0x8 +ASMCONSTANTS_C_ASSERT(ArrayBase__m_NumComponents == offsetof(ArrayBase, m_NumComponents)); + +#define PtrArray__m_Array 0x10 +ASMCONSTANTS_C_ASSERT(PtrArray__m_Array == offsetof(PtrArray, m_Array)); + +#define TypeHandle_CanCast 0x1 // TypeHandle::CanCast + +//========================================= + + + +#ifdef FEATURE_COMINTEROP + +#define SIZEOF__ComMethodFrame 0x70 +ASMCONSTANTS_C_ASSERT(SIZEOF__ComMethodFrame == sizeof(ComMethodFrame)); + +#define UnmanagedToManagedFrame__m_pvDatum 0x10 +ASMCONSTANTS_C_ASSERT(UnmanagedToManagedFrame__m_pvDatum == offsetof(UnmanagedToManagedFrame, m_pvDatum)); + +#endif // FEATURE_COMINTEROP + + +#define REDIRECTSTUB_SP_OFFSET_CONTEXT 0 + +#define CONTEXT_Pc 0x108 +ASMCONSTANTS_C_ASSERT(CONTEXT_Pc == offsetof(T_CONTEXT,Pc)) + +#define SIZEOF__FaultingExceptionFrame (SIZEOF__Frame + 0x10 + SIZEOF__CONTEXT) +#define FaultingExceptionFrame__m_fFilterExecuted SIZEOF__Frame +ASMCONSTANTS_C_ASSERT(SIZEOF__FaultingExceptionFrame == sizeof(FaultingExceptionFrame)); +ASMCONSTANTS_C_ASSERT(FaultingExceptionFrame__m_fFilterExecuted == offsetof(FaultingExceptionFrame, m_fFilterExecuted)); + +#define SIZEOF__FixupPrecode 40 +#define MethodDesc_ALIGNMENT_SHIFT 3 + +ASMCONSTANTS_C_ASSERT(MethodDesc_ALIGNMENT_SHIFT == MethodDesc::ALIGNMENT_SHIFT); + +#define ResolveCacheElem__pMT 0x00 +#define ResolveCacheElem__token 0x08 +#define ResolveCacheElem__target 0x10 +#define ResolveCacheElem__pNext 0x18 +ASMCONSTANTS_C_ASSERT(ResolveCacheElem__target == offsetof(ResolveCacheElem, target)); +ASMCONSTANTS_C_ASSERT(ResolveCacheElem__pNext == offsetof(ResolveCacheElem, pNext)); + +#define DomainLocalModule__m_pDataBlob 0x30 +#define DomainLocalModule__m_pGCStatics 0x20 +ASMCONSTANTS_C_ASSERT(DomainLocalModule__m_pDataBlob == offsetof(DomainLocalModule, m_pDataBlob)); +ASMCONSTANTS_C_ASSERT(DomainLocalModule__m_pGCStatics == offsetof(DomainLocalModule, m_pGCStatics)); + + +// For JIT_PInvokeBegin and JIT_PInvokeEnd helpers +#define Frame__m_Next 0x08 +ASMCONSTANTS_C_ASSERT(Frame__m_Next == offsetof(Frame, m_Next)) + +#define InlinedCallFrame__m_Datum 0x10 +ASMCONSTANTS_C_ASSERT(InlinedCallFrame__m_Datum == offsetof(InlinedCallFrame, m_Datum)) + +#define InlinedCallFrame__m_pCallSiteSP 0x20 +ASMCONSTANTS_C_ASSERT(InlinedCallFrame__m_pCallSiteSP == offsetof(InlinedCallFrame, m_pCallSiteSP)) + +#define InlinedCallFrame__m_pCallerReturnAddress 0x28 +ASMCONSTANTS_C_ASSERT(InlinedCallFrame__m_pCallerReturnAddress == offsetof(InlinedCallFrame, m_pCallerReturnAddress)) + +#define InlinedCallFrame__m_pCalleeSavedFP 0x30 +ASMCONSTANTS_C_ASSERT(InlinedCallFrame__m_pCalleeSavedFP == offsetof(InlinedCallFrame, m_pCalleeSavedFP)) + +#define InlinedCallFrame__m_pThread 0x38 +ASMCONSTANTS_C_ASSERT(InlinedCallFrame__m_pThread == offsetof(InlinedCallFrame, m_pThread)) + +#define FixupPrecodeData__Target 0x00 +ASMCONSTANTS_C_ASSERT(FixupPrecodeData__Target == offsetof(FixupPrecodeData, Target)) + +#define FixupPrecodeData__MethodDesc 0x08 +ASMCONSTANTS_C_ASSERT(FixupPrecodeData__MethodDesc == offsetof(FixupPrecodeData, MethodDesc)) + +#define FixupPrecodeData__PrecodeFixupThunk 0x10 +ASMCONSTANTS_C_ASSERT(FixupPrecodeData__PrecodeFixupThunk == offsetof(FixupPrecodeData, PrecodeFixupThunk)) + +#define StubPrecodeData__Target 0x08 +ASMCONSTANTS_C_ASSERT(StubPrecodeData__Target == offsetof(StubPrecodeData, Target)) + +#define StubPrecodeData__MethodDesc 0x00 +ASMCONSTANTS_C_ASSERT(StubPrecodeData__MethodDesc == offsetof(StubPrecodeData, MethodDesc)) + +#define CallCountingStubData__RemainingCallCountCell 0x00 +ASMCONSTANTS_C_ASSERT(CallCountingStubData__RemainingCallCountCell == offsetof(CallCountingStubData, RemainingCallCountCell)) + +#define CallCountingStubData__TargetForMethod 0x08 +ASMCONSTANTS_C_ASSERT(CallCountingStubData__TargetForMethod == offsetof(CallCountingStubData, TargetForMethod)) + +#define CallCountingStubData__TargetForThresholdReached 0x10 +ASMCONSTANTS_C_ASSERT(CallCountingStubData__TargetForThresholdReached == offsetof(CallCountingStubData, TargetForThresholdReached)) + +#undef ASMCONSTANTS_RUNTIME_ASSERT +#undef ASMCONSTANTS_C_ASSERT diff --git a/src/coreclr/vm/riscv64/asmhelpers.S b/src/coreclr/vm/riscv64/asmhelpers.S index 3515f38c8120d7..cc063af47a2ae6 100644 --- a/src/coreclr/vm/riscv64/asmhelpers.S +++ b/src/coreclr/vm/riscv64/asmhelpers.S @@ -1,7 +1,955 @@ // Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. +// The .NET Foundation llacenses this file to you under the MIT license. #include "asmconstants.h" #include "unixasmmacros.inc" -#error "TODO-RISCV64: missing implementation" +LEAF_ENTRY GetCurrentIP, _TEXT + addi a0, ra, 0 + jalr x0, ra, 0 +LEAF_END GetCurrentIP, _TEXT + +// LPVOID __stdcall GetCurrentSP(void)// +LEAF_ENTRY GetCurrentSP, _TEXT + addi a0, sp, 0 + jalr x0, ra, 0 +LEAF_END GetCurrentSP, _TEXT + +//----------------------------------------------------------------------------- +// The following Macros help in WRITE_BARRIER Implementations +// WRITE_BARRIER_ENTRY +// +// Declare the start of a write barrier function. Use similarly to NESTED_ENTRY. This is the only legal way +// to declare a write barrier function. +// +.macro WRITE_BARRIER_ENTRY name + LEAF_ENTRY \name, _TEXT +.endm + +// WRITE_BARRIER_END +// +// The partner to WRITE_BARRIER_ENTRY, used llake NESTED_END. +// +.macro WRITE_BARRIER_END name + LEAF_END_MARKED \name, _TEXT +.endm + +// void JIT_UpdateWriteBarrierState(bool skipEphemeralCheck, size_t writeableOffset) +// +// Update shadow copies of the various state info required for barrier +// +// State info is contained in a llateral pool at the end of the function +// Placed in text section so that it is close enough to use ldr llateral and still +// be relocatable. Ellaminates need for PREPARE_EXTERNAL_VAR in hot code. +// +// Allagn and group state info together so it fits in a single cache line +// and each entry can be written atomically +// +WRITE_BARRIER_ENTRY JIT_UpdateWriteBarrierState + // a0-a7 will contain intended new state + // t0 will preserve skipEphemeralCheck + // t2 will be used for pointers + + addi t0, a0, 0 + addi t1, a1, 0 + + lla a0, g_card_table + ld a0, 0(a0) + +#ifdef FEATURE_MANUALLY_MANAGED_CARD_BUNDLES + lla a1, g_card_bundle_table + ld a1, 0(a1) +#endif + +#ifdef WRITE_BARRIER_CHECK + lla a2, g_GCShadow + ld a2, 0(a2) +#endif + +#ifdef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP + lla a3, g_sw_ww_table + ld a3, 0(a3) +#endif + + lla a4, g_ephemeral_low + ld a4, 0(a4) + + lla a5, g_ephemeral_high + ld a5, 0(a5) + + beq t0, zero, LOCAL_LABEL(EphemeralCheckEnabled) + + ori a4, zero, 0 + addi a5, zero, -1 +LOCAL_LABEL(EphemeralCheckEnabled): + + lla a6, g_lowest_address + ld a6, 0(a6) + + lla a7, g_highest_address + ld a7, 0(a7) + + // Update wbs state + lla t2, JIT_WriteBarrier_Table_Loc + ld t2, 0(t2) + add t2, t2, t1 + + sd a0, 0(t2) + sd a1, 8(t2) + sd a2, 16(t2) + sd a3, 24(t2) + sd a4, 32(t2) + sd a5, 40(t2) + sd a6, 48(t2) + sd a7, 56(t2) + + EPILOG_RETURN + +WRITE_BARRIER_END JIT_UpdateWriteBarrierState + +// ---------------------------------------------------------------------------------------- +// __declspec(naked) void F_CALL_CONV JIT_WriteBarrier_Callable(Object **dst, Object* val) +LEAF_ENTRY JIT_WriteBarrier_Callable, _TEXT + // Setup args for JIT_WriteBarrier. a0 = dst ; a1 = val + addi t3, a0, 0 // t3 = dst + addi t4, a1, 0 // t4 = val + + // Branch to the write barrier + lla t1, JIT_WriteBarrier_Loc + ld t1, 0(t1) + jalr x0, t1, 0 +LEAF_END JIT_WriteBarrier_Callable, _TEXT + + +.balign 64 // Align to power of two at least as big as patchable literal pool so that it fits optimally in cache line +// ------------------------------------------------------------------ +// Start of the writeable code region +LEAF_ENTRY JIT_PatchedCodeStart, _TEXT + jalr x0, ra, 0 +LEAF_END JIT_PatchedCodeStart, _TEXT + +// void JIT_ByRefWriteBarrier +// +// On entry: +// t5 : the source address (points to object reference to write) +// t3: the destination address (object reference written here) +// +// On exit: +// t5 : incremented by 8 +// t4 : trashed +// + +// void JIT_ByRefWriteBarrier +WRITE_BARRIER_ENTRY JIT_ByRefWriteBarrier + ld t4, 0(t5) + addi t5, t5, 8 + tail C_FUNC(JIT_CheckedWriteBarrier) +WRITE_BARRIER_END JIT_ByRefWriteBarrier + +//----------------------------------------------------------------------------- +// Simple WriteBarriers +// void JIT_CheckedWriteBarrier(Object** dst, Object* src) +// +// On entry: +// t3 : the destination address (LHS of the assignment) +// t4 : the object reference (RHS of the assignment) +// +// On exit: +// t1 : trashed +// t0 : trashed +// t6 : trashed +// t3 : trashed (incremented by 8 to implement JIT_ByRefWriteBarrier contract) +// + +WRITE_BARRIER_ENTRY JIT_CheckedWriteBarrier + lla t6, wbs_lowest_address + ld t6, 0(t6) + slt t6, t3, t6 + + lla t1, wbs_highest_address + ld t1, 0(t1) + slt t0, t1, t3 + or t6, t0, t6 + beq t6, zero, C_FUNC(JIT_WriteBarrier) + + sd t4, 0(t3) + addi t3, t3, 8 + jalr x0, ra, 0 +WRITE_BARRIER_END JIT_CheckedWriteBarrier + +// void JIT_WriteBarrier(Object** dst, Object* src) +// On entry: +// t3 : the destination address (LHS of the assignment) +// t4 1 the object reference (RHS of the assignment) +// +// On exit: +// t0 : trashed +// t1 : trashed +// t6 : trashed +// t4 : trashed +// t3 : trashed (incremented by 8 to implement JIT_ByRefWriteBarrier contract) +// +WRITE_BARRIER_ENTRY JIT_WriteBarrier + + // TODO: sync_release (runtime detection required) + fence rw, rw + + sd t4, 0(t3) + +#ifdef WRITE_BARRIER_CHECK + // Update GC Shadow Heap + + // Do not perform the work if g_GCShadow is 0 + lla t1, wbs_GCShadow + ld t1, 0(t1) + + beq t1, zero, LOCAL_LABEL(ShadowUpdateDisabled) + + // Compute address of shadow heap location: + // pShadow = g_GCShadow + ($t3 - g_lowest_address) + lla t6, wbs_lowest_address + ld t6, 0(t6) + + sub t6, t3, t6 + add t0, t6, t1 + + // if (pShadow >= g_GCShadowEnd) goto end + lla t6, g_GCShadowEnd + ld t6, 0(t6) + + slt t6, t0, t6 + beq t6, zero, LOCAL_LABEL(ShadowUpdateEnd) + + // *pShadow = $t4 + sd t4, 0(t0) + + // Ensure that the write to the shadow heap occurs before the read from the GC heap so that race + // conditions are caught by INVALIDGCVALUE. + fence rw, rw + + // if (*t3 == t4) goto end + ld t6, 0(t3) + beq t6, t4, LOCAL_LABEL(ShadowUpdateEnd) + + // *pShadow = INVALIDGCVALUE (0xcccccccd) + li t6, 0xcccccccd + sd t6, 0(t0) +LOCAL_LABEL(ShadowUpdateEnd): +LOCAL_LABEL(ShadowUpdateDisabled): +#endif + +#ifdef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP + // Update the write watch table if necessary + + lla t6, wbs_sw_ww_table + ld t6, 0(t6) + beq t6, zero, LOCAL_LABEL(CheckCardTable) + + srli t4, t3, 0xc + add t6, t6, t4 // SoftwareWriteWatch::AddressToTableByteIndexShift + lb t4, 0(t6) + bne t4, zero, LOCAL_LABEL(CheckCardTable) + + ori t4, zero, 0xFF + sb t4, 0(t6) + +LOCAL_LABEL(CheckCardTable): +#endif + // Branch to Exit if the reference is not in the Gen0 heap + lla t6, wbs_ephemeral_low + ld t6, 0(t6) + beq t6, zero, LOCAL_LABEL(SkipEphemeralCheck) + + slt t0, t4, t6 + lla t6, wbs_ephemeral_high + ld t6, 0(t6) + slt t1, t6, t4 + or t0, t1, t0 + bne t0, zero, LOCAL_LABEL(Exit) + +LOCAL_LABEL(SkipEphemeralCheck): + // Check if we need to update the card table + lla t6, wbs_card_table + ld t6, 0(t6) + srli t0, t3, 11 + add t4, t6, t0 + lbu t1, 0(t4) + ori t0, zero, 0xFF + beq t1, t0, LOCAL_LABEL(Exit) + + sb t0, 0(t4) + +#ifdef FEATURE_MANUALLY_MANAGED_CARD_BUNDLES + // Check if we need to update the card bundle table + lla t6, wbs_card_bundle_table + ld t6, 0(t6) + srli t0, t3, 21 + add t4, t6, t0 + + lbu t6, 0(t4) + ori t0, zero, 0xFF + beq t6, t0, LOCAL_LABEL(Exit) + + sb t0, 0(t4) +#endif +LOCAL_LABEL(Exit): + addi t3, t3, 8 + jalr x0, ra, 0 +WRITE_BARRIER_END JIT_WriteBarrier + +// Begin patchable literal pool + .balign 64 // Align to power of two at least as big as patchable literal pool so that it fits optimally in cache line +WRITE_BARRIER_ENTRY JIT_WriteBarrier_Table +wbs_begin: +wbs_card_table: + .quad 0 +wbs_card_bundle_table: + .quad 0 +wbs_GCShadow: + .quad 0 +wbs_sw_ww_table: + .quad 0 +wbs_ephemeral_low: + .quad 0 +wbs_ephemeral_high: + .quad 0 +wbs_lowest_address: + .quad 0 +wbs_highest_address: + .quad 0 +WRITE_BARRIER_END JIT_WriteBarrier_Table + +// ------------------------------------------------------------------ +// End of the writeable code region +LEAF_ENTRY JIT_PatchedCodeLast, _TEXT + jalr x0, ra, 0 +LEAF_END JIT_PatchedCodeLast, _TEXT + + +// +// If a preserved register were pushed onto the stack between +// the managed caller and the H_M_F, ptrS0_S8 will point to its +// location on the stack and it would have been updated on the +// stack by the GC already and it will be popped back into the +// appropriate register when the appropriate epilog is run. +// +// Otherwise, the register is preserved across all the code +// in this HCALL or FCALL, so we need to update those registers +// here because the GC will have updated our copies in the +// frame. +// +// So, if ptrS0_S8 points into the MachState, we need to update +// the register here. That's what this macro does. +// +.macro RestoreRegMS idx, reg + // Incoming: + // + // a0 = address of MachState + // + // idx: Index of the callee register + // s0/fp: 0, s1: 1, s3-s11: 4-11, gp: 12 tp: 13 + // + // reg: Register name (e.g. s0, s1, etc) + // + // Get the address of the specified captured register from machine state + addi a2, a0, (MachState__captureCalleeSavedRegisters + (\idx * 8)) + + //// Get the content of specified preserved register pointer from machine state + ld a3, (MachState__ptrCalleeSavedRegisters + (\idx * 8))(a0) + + bne a2, a3, LOCAL_LABEL(NoRestore_\reg) + + ld \reg, 0(a2) +LOCAL_LABEL(NoRestore_\reg): + +.endm + +NESTED_ENTRY ThePreStub, _TEXT, NoHandler + PROLOG_WITH_TRANSITION_BLOCK + + addi a1, METHODDESC_REGISTER, 0 // pMethodDesc + + addi a0, sp, __PWTB_TransitionBlock // pTransitionBlock + call PreStubWorker + addi t4, a0, 0 + + EPILOG_WITH_TRANSITION_BLOCK_TAILCALL + EPILOG_BRANCH_REG t4 +NESTED_END ThePreStub, _TEXT + +// ------------------------------------------------------------------\ + +// EXTERN_C int __fastcall HelperMethodFrameRestoreState( +// INDEBUG_COMMA(HelperMethodFrame *pFrame) +// MachState *pState +// ) +LEAF_ENTRY HelperMethodFrameRestoreState, _TEXT +#ifdef _DEBUG + addi a0, a1, 0 +#endif + + // If machine state is invalid, then simply exit + lw a1, MachState__isValid(a0) + beq a1, zero, LOCAL_LABEL(Done) + + // manually assign index + // s0/fp: 0, s1: 1, s3-s11: 4-11, gp: 12 tp: 13 + RestoreRegMS 0, s0 + RestoreRegMS 1, s1 + RestoreRegMS 2, s2 + RestoreRegMS 3, s3 + RestoreRegMS 4, s4 + RestoreRegMS 5, s5 + RestoreRegMS 6, s6 + RestoreRegMS 7, s7 + RestoreRegMS 8, s8 + RestoreRegMS 9, s9 + RestoreRegMS 10, s10 + RestoreRegMS 11, s11 + RestoreRegMS 12, tp + RestoreRegMS 13, gp +LOCAL_LABEL(Done): + // Its imperative that the return value of HelperMethodFrameRestoreState is zero + // as it is used in the state machine to loop until it becomes zero. + // Refer to HELPER_METHOD_FRAME_END macro for details. + addi a0, zero, 0 + jalr x0, ra, 0 +LEAF_END HelperMethodFrameRestoreState, _TEXT + +//----------------------------------------------------------------------------- +// This routine captures the machine state. It is used by helper method frame +//----------------------------------------------------------------------------- +//void LazyMachStateCaptureState(struct LazyMachState *pState)// +LEAF_ENTRY LazyMachStateCaptureState, _TEXT + // marks that this is not yet valid + sw zero, (MachState__isValid)(a0) + + sd ra, (LazyMachState_captureIp)(a0) + + // save sp register. + sd sp, (LazyMachState_captureSp)(a0) + + // save non-volatile registers that can contain object references + addi a1, a0, LazyMachState_captureCalleeSavedRegisters + + sd s0, 0(a1) + sd s1, 8(a1) + sd s2, 16(a1) + sd s3, 24(a1) + sd s4, 32(a1) + sd s5, 40(a1) + sd s6, 48(a1) + sd s7, 56(a1) + sd s8, 64(a1) + sd s9, 72(a1) + sd s10, 80(a1) + sd s11, 88(a1) + sd tp, 96(a1) + sd gp, 104(a1) + + jalr x0, ra, 0 +LEAF_END LazyMachStateCaptureState, _TEXT + +// ------------------------------------------------------------------ +// The call in ndirect import precode points to this function. +NESTED_ENTRY NDirectImportThunk, _TEXT, NoHandler + PROLOG_SAVE_REG_PAIR_INDEXED fp, ra, 0xa0 + SAVE_ARGUMENT_REGISTERS sp, 0x20 + SAVE_FLOAT_ARGUMENT_REGISTERS sp, 0x60 + + addi a0, t2, 0 + call C_FUNC(NDirectImportWorker) + addi t4, a0, 0 + + // pop the stack and restore original register state + RESTORE_FLOAT_ARGUMENT_REGISTERS sp, 0x60 + RESTORE_ARGUMENT_REGISTERS sp, 0x20 + //EPILOG_RESTORE_REG gp, 16 + EPILOG_RESTORE_REG_PAIR_INDEXED fp, ra, 0xa0 + + // If we got back from NDirectImportWorker, the MD has been successfully + // linked. Proceed to execute the original DLL call. + EPILOG_BRANCH_REG t4 +NESTED_END NDirectImportThunk, _TEXT + +// void SinglecastDelegateInvokeStub(Delegate *pThis) +LEAF_ENTRY SinglecastDelegateInvokeStub, _TEXT + beq a0, zero, LOCAL_LABEL(LNullThis) + + ld t4, (DelegateObject___methodPtr)(a0) + ld a0, (DelegateObject___target)(a0) + jalr x0, t4, 0 + +LOCAL_LABEL(LNullThis): + addi a0, zero, CORINFO_NullReferenceException_ASM + tail JIT_InternalThrow +LEAF_END SinglecastDelegateInvokeStub, _TEXT + +// ------------------------------------------------------------------ +// ThePreStubPatch() +LEAF_ENTRY ThePreStubPatch, _TEXT +.globl C_FUNC(ThePreStubPatchLabel) +C_FUNC(ThePreStubPatchLabel): + jalr x0, ra, 0 +LEAF_END ThePreStubPatch, _TEXT + +NESTED_ENTRY TheUMEntryPrestub, _TEXT, UnhandledExceptionHandlerUnix + // Save arguments and return address + PROLOG_SAVE_REG_PAIR_INDEXED fp, ra, 0xa0 + //PROLOG_SAVE_REG gp, 16 + SAVE_ARGUMENT_REGISTERS sp, 32 + SAVE_FLOAT_ARGUMENT_REGISTERS sp, 96 + + + addi a0, t2, 0 + call TheUMEntryPrestubWorker + addi t4, a0, 0 + + // pop the stack and restore original register state + RESTORE_FLOAT_ARGUMENT_REGISTERS sp, 96 + RESTORE_ARGUMENT_REGISTERS sp, 32 + //EPILOG_RESTORE_REG gp, 16 + EPILOG_RESTORE_REG_PAIR_INDEXED fp, ra, 0xa0 + + // and tailcall to the actual method + EPILOG_BRANCH_REG t4 +NESTED_END TheUMEntryPrestub, _TEXT + +// ------------------------------------------------------------------ +// void* JIT_GetSharedGCStaticBase(SIZE_T moduleDomainID, DWORD dwClassDomainID) + +LEAF_ENTRY JIT_GetSharedGCStaticBase_SingleAppDomain, _TEXT + // If class is not initialized, bail to C++ helper + addi a2, a0, DomainLocalModule__m_pDataBlob + add a2, a2, a1 + lb a2, 0(a2) + andi t5, a2, 1 + beq t5, zero, LOCAL_LABEL(JIT_GetSharedGCStaticBase_SingleAppDomain_CallHelper) + + ld a0, DomainLocalModule__m_pGCStatics(a0) + jalr x0, ra, 0 + +LOCAL_LABEL(JIT_GetSharedGCStaticBase_SingleAppDomain_CallHelper): + // Tail call JIT_GetSharedGCStaticBase_Helper + call JIT_GetSharedGCStaticBase_Helper +LEAF_END JIT_GetSharedGCStaticBase_SingleAppDomain, _TEXT + +// ------------------------------------------------------------------ +// ResolveWorkerChainLookupAsmStub +// +// This method will perform a quick chained lookup of the entry if the +// initial cache lookup fails. +// +// On Entry: +// t1 contains the pointer to the current ResolveCacheElem +// t5 contains the address of the indirection (and the flags in the low two bits) +// t2 contains our contract the DispatchToken +// Must be preserved: +// a0 contains the instance object ref that we are making an interface call on +// t1 Must point to a ResolveCacheElem [For Sanity] +// [a1-a7] contains any additional register arguments for the interface method +// +// Loaded from a0 +// t6 contains our type the MethodTable (from object ref in a0) +// +// On Exit: +// a0, [a1-a7] arguments for the interface implementation target +// +// On Exit (to ResolveWorkerAsmStub): +// t5 contains the address of the indirection and the flags in the low two bits. +// t2 contains our contract (DispatchToken) +// t4 will be trashed +// + +#define BACKPATCH_FLAG 1 +#define PROMOTE_CHAIN_FLAG 2 + +NESTED_ENTRY ResolveWorkerChainLookupAsmStub, _TEXT, NoHandler + andi t4, t5, BACKPATCH_FLAG // First we check if t5 has the BACKPATCH_FLAG set + bne t4, zero, LOCAL_LABEL(Fail) // If the BACKPATCH_FLAGS is set we will go directly to the ResolveWorkerAsmStub + + ld t6, 0(a0) // retrieve the MethodTable from the object ref in a0 +LOCAL_LABEL(MainLoop): + ld t1, (ResolveCacheElem__pNext)(t1) // t1 <= the next entry in the chain + beq t1, zero, LOCAL_LABEL(Fail) + + ld t4, 0(t1) + // compare our MT with the one in the ResolveCacheElem + bne t4, t6, LOCAL_LABEL(MainLoop) + + ld t4, 8(t1) + // compare our DispatchToken with one in the ResolveCacheElem + bne t2, t4, LOCAL_LABEL(MainLoop) + +LOCAL_LABEL(Success): + PREPARE_EXTERNAL_VAR g_dispatch_cache_chain_success_counter, t6 + ld t4, 0(t6) + addi t4, t4, -1 + sd t4, 0(t6) + blt t4, zero, LOCAL_LABEL(Promote) + + ld t4, (ResolveCacheElem__target)(t1) // get the ImplTarget + jalr x0, t4, 0 // branch to interface implementation target + +LOCAL_LABEL(Promote): + // Move this entry to head position of the chain + addi t4, zero, 256 + sd t4, 0(t6) // be quick to reset the counter so we don't get a bunch of contending threads + ori t5, t5, PROMOTE_CHAIN_FLAG // set PROMOTE_CHAIN_FLAG + addi t2, t1, 0 // We pass the ResolveCacheElem to ResolveWorkerAsmStub instead of the DispatchToken + +LOCAL_LABEL(Fail): + tail C_FUNC(ResolveWorkerAsmStub) // call the ResolveWorkerAsmStub method to transition into the VM +NESTED_END ResolveWorkerChainLookupAsmStub, _TEXT + +// ------------------------------------------------------------------ +// void ResolveWorkerAsmStub(args in regs a0-a7 & stack, t5:IndirectionCellAndFlags, t2:DispatchToken) +// +// The stub dispatch thunk which transfers control to VSD_ResolveWorker. +NESTED_ENTRY ResolveWorkerAsmStub, _TEXT, NoHandler + PROLOG_WITH_TRANSITION_BLOCK + + addi a2, t2, 0 // DispatchToken + addi a0, sp, __PWTB_TransitionBlock // pTransitionBlock + srli a1, t5, 2 + andi a3, t5, 3 // flag + slli a1, a1, 2 + call C_FUNC(VSD_ResolveWorker) + addi t4, a0, 0 + + EPILOG_WITH_TRANSITION_BLOCK_TAILCALL + + EPILOG_BRANCH_REG t4 +NESTED_END ResolveWorkerAsmStub, _TEXT + +// ------------------------------------------------------------------ +// void* JIT_GetSharedNonGCStaticBaseNoCtor(SIZE_T moduleDomainID, DWORD dwClassDomainID) + +LEAF_ENTRY JIT_GetSharedNonGCStaticBaseNoCtor_SingleAppDomain, _TEXT + jalr x0, ra, 0 +LEAF_END JIT_GetSharedNonGCStaticBaseNoCtor_SingleAppDomain, _TEXT + +// ------------------------------------------------------------------ +// void* JIT_GetSharedGCStaticBaseNoCtor(SIZE_T moduleDomainID, DWORD dwClassDomainID) + +LEAF_ENTRY JIT_GetSharedGCStaticBaseNoCtor_SingleAppDomain, _TEXT + ld a0, (DomainLocalModule__m_pGCStatics)(a0) + jalr x0, ra, 0 +LEAF_END JIT_GetSharedGCStaticBaseNoCtor_SingleAppDomain, _TEXT + + +#ifdef FEATURE_HIJACK +// ------------------------------------------------------------------ +// Hijack function for functions which return a scalar type or a struct (value type) +NESTED_ENTRY OnHijackTripThread, _TEXT, NoHandler + PROLOG_SAVE_REG_PAIR_INDEXED fp, ra, 0x88 + + // Spill callee saved registers + PROLOG_SAVE_REG_PAIR s1, s2, 16 + PROLOG_SAVE_REG_PAIR s3, s4, 32 + PROLOG_SAVE_REG_PAIR s5, s6, 48 + PROLOG_SAVE_REG_PAIR s7, s8, 64 + PROLOG_SAVE_REG_PAIR s9, s10, 80 + PROLOG_SAVE_REG s11, 96 + + // save any integral return value(s) + sd a0, 104(sp) + sd a1, 112(sp) + + // save any FP/HFA return value(s) + fsd f0, 120(sp) + fsd f1, 128(sp) + + addi a0, sp, 0 + call C_FUNC(OnHijackWorker) + + // restore callee saved registers + + // restore any integral return value(s) + ld a0, 104(sp) + ld a1, 112(sp) + + // restore any FP/HFA return value(s) + fld f0, 120(sp) + fld f1, 128(sp) + + EPILOG_RESTORE_REG_PAIR s1, s2, 16 + EPILOG_RESTORE_REG_PAIR s3, s4, 32 + EPILOG_RESTORE_REG_PAIR s5, s6, 48 + EPILOG_RESTORE_REG_PAIR s7, s8, 64 + EPILOG_RESTORE_REG_PAIR s9, s10, 80 + EPILOG_RESTORE_REG s11, 96 + EPILOG_RESTORE_REG_PAIR_INDEXED fp, ra, 0x88 + EPILOG_RETURN +NESTED_END OnHijackTripThread, _TEXT + +#endif // FEATURE_HIJACK + +// ------------------------------------------------------------------ +// Redirection Stub for GC in fully interruptible method +//GenerateRedirectedHandledJITCaseStub GCThreadControl +// ------------------------------------------------------------------ +//GenerateRedirectedHandledJITCaseStub DbgThreadControl +// ------------------------------------------------------------------ +//GenerateRedirectedHandledJITCaseStub UserSuspend + +#ifdef _DEBUG +// ------------------------------------------------------------------ +// Redirection Stub for GC Stress +GenerateRedirectedHandledJITCaseStub GCStress +#endif + + +// ------------------------------------------------------------------ +// This helper enables us to call into a funclet after restoring Fp register +NESTED_ENTRY CallEHFunclet, _TEXT, NoHandler + // On entry: + // + // a0 = throwable + // a1 = PC to invoke + // a2 = address of s0 register in CONTEXT record// used to restore the non-volatile registers of CrawlFrame + // a3 = address of the location where the SP of funclet's caller (i.e. this helper) should be saved. + // + + PROLOG_SAVE_REG_PAIR_INDEXED fp, ra, 120, 0 + + // Spill callee saved registers + PROLOG_SAVE_REG_PAIR s1, s2, 16 + PROLOG_SAVE_REG_PAIR s3, s4, 32 + PROLOG_SAVE_REG_PAIR s5, s6, 48 + PROLOG_SAVE_REG_PAIR s7, s8, 64 + PROLOG_SAVE_REG_PAIR s9, s10, 80 + PROLOG_SAVE_REG_PAIR s11, gp, 96 + PROLOG_SAVE_REG tp, 112 + + // Save the SP of this function + sd sp, 0(a3) + + ld gp, (-40)(a2) // offset of tp in PCONTEXT relative to S0. + ld tp, (-32)(a2) // offset of tp in PCONTEXT relative to S0. + ld fp, 0(a2) // offset of fp in PCONTEXT relative to S0. + ld s1, 8(a2) + ld s2, 80(a2) + ld s3, 88(a2) + ld s4, 96(a2) + ld s5, 104(a2) + ld s6, 112(a2) + ld s7, 120(a2) + ld s8, 128(a2) + ld s9, 136(a2) + ld s10, 144(a2) + ld s11, 152(a2) + + // Invoke the funclet + jalr ra, a1, 0 + + EPILOG_RESTORE_REG_PAIR s1, s2, 16 + EPILOG_RESTORE_REG_PAIR s3, s4, 32 + EPILOG_RESTORE_REG_PAIR s5, s6, 48 + EPILOG_RESTORE_REG_PAIR s7, s8, 64 + EPILOG_RESTORE_REG_PAIR s9, s10, 80 + EPILOG_RESTORE_REG_PAIR s11, gp, 96 + EPILOG_RESTORE_REG tp, 112 + + EPILOG_RESTORE_REG_PAIR_INDEXED fp, ra, 120 + EPILOG_RETURN +NESTED_END CallEHFunclet, _TEXT + +// This helper enables us to call into a filter funclet by passing it the CallerSP to lookup the +// frame pointer for accessing the locals in the parent method. +NESTED_ENTRY CallEHFilterFunclet, _TEXT, NoHandler + PROLOG_SAVE_REG_PAIR_INDEXED fp, ra, 16 + + // On entry: + // + // a0 = throwable + // a1 = SP of the caller of the method/funclet containing the filter + // a2 = PC to invoke + // a3 = address of the location where the SP of funclet's caller (i.e. this helper) should be saved. + // + // Save the SP of this function + sd fp, 0(a3) + // Invoke the filter funclet + jalr ra, a2, 0 + + EPILOG_RESTORE_REG_PAIR_INDEXED fp, ra, 16 + EPILOG_RETURN +NESTED_END CallEHFilterFunclet, _TEXT + +#ifdef FEATURE_COMINTEROP +// Function used by COM interop to get floating point return value (since it's not in the same +// register(s) as non-floating point values). +// +// On entry// +// a0 : size of the FP result (4 or 8 bytes) +// a1 : pointer to 64-bit buffer to receive result +// +// On exit: +// buffer pointed to by a1 on entry contains the float or double argument as appropriate +// +LEAF_ENTRY getFPReturn, _TEXT + fsd f0, 0(a1) +LEAF_END getFPReturn, _TEXT + +// ------------------------------------------------------------------ +// Function used by COM interop to set floating point return value (since it's not in the same +// register(s) as non-floating point values). +// +LEAF_ENTRY setFPReturn, _TEXT + fmv.d.x f0, a1 +LEAF_END setFPReturn, _TEXT + +#endif // FEATURE_COMINTEROP + +// +// JIT Static access helpers when coreclr host specifies single appdomain flag +// + +// ------------------------------------------------------------------ +// void* JIT_GetSharedNonGCStaticBase(SIZE_T moduleDomainID, DWORD dwClassDomainID) + +LEAF_ENTRY JIT_GetSharedNonGCStaticBase_SingleAppDomain, _TEXT + // If class is not initialized, bail to C++ helper + // dext a1, a1, 0, 32 + addi a2, a0, DomainLocalModule__m_pDataBlob + + add a2, a2, a1 + lb a2, 0(a2) + andi t4, a2, 1 + beq t4, zero, LOCAL_LABEL(JIT_GetSharedNonGCStaticBase_SingleAppDomain_CallHelper) + + jalr x0, ra, 0 + +LOCAL_LABEL(JIT_GetSharedNonGCStaticBase_SingleAppDomain_CallHelper): + // Tail call JIT_GetSharedNonGCStaticBase_Helper + tail JIT_GetSharedNonGCStaticBase_Helper +LEAF_END JIT_GetSharedNonGCStaticBase_SingleAppDomain, _TEXT + +#ifdef FEATURE_READYTORUN + +NESTED_ENTRY DelayLoad_MethodCall_FakeProlog, _TEXT, NoHandler +C_FUNC(DelayLoad_MethodCall): + .global C_FUNC(DelayLoad_MethodCall) + PROLOG_WITH_TRANSITION_BLOCK + + addi a1, t5, 0 // Indirection cell + addi a2, t0, 0 // sectionIndex + addi a3, t1, 0 // Module* + + addi a0, sp, __PWTB_TransitionBlock // pTransitionBlock + call C_FUNC(ExternalMethodFixupWorker) + addi t4, a0, 0 + + EPILOG_WITH_TRANSITION_BLOCK_TAILCALL + PATCH_LABEL ExternalMethodFixupPatchLabel + EPILOG_BRANCH_REG t4 +NESTED_END DelayLoad_MethodCall_FakeProlog, _TEXT + + +.macro DynamicHelper frameFlags, suffix +NESTED_ENTRY DelayLoad_Helper\suffix\()_FakeProlog, _TEXT, NoHandler +DelayLoad_Helper\suffix: + .global DelayLoad_Helper\suffix + + PROLOG_WITH_TRANSITION_BLOCK + + //DynamicHelperWorker(TransitionBlock * pTransitionBlock, TADDR * pCell, + // DWORD sectionIndex, Module * pModule, INT frameFlags) + addi a1, t5, 0 // Indirection cell + addi a2, t0, 0 // sectionIndex + addi a3, t1, 0 // Module* + addi a4, x0, \frameFlags + + addi a0, sp, __PWTB_TransitionBlock // pTransitionBlock + call DynamicHelperWorker + + bne a0, x0, LOCAL_LABEL(FakeProlog\suffix\()_0) + + ld a0, __PWTB_ArgumentRegisters(sp) + EPILOG_WITH_TRANSITION_BLOCK_RETURN +LOCAL_LABEL(FakeProlog\suffix\()_0): + addi t4, a0, 0 + EPILOG_WITH_TRANSITION_BLOCK_TAILCALL + EPILOG_BRANCH_REG t4 + +NESTED_END DelayLoad_Helper\suffix\()_FakeProlog, _TEXT +.endm + +DynamicHelper DynamicHelperFrameFlags_Default +DynamicHelper DynamicHelperFrameFlags_ObjectArg, _Obj +DynamicHelper DynamicHelperFrameFlags_ObjectArg | DynamicHelperFrameFlags_ObjectArg2, _ObjObj +#endif + + +#ifdef PROFILING_SUPPORTED + +// ------------------------------------------------------------------ +LEAF_ENTRY JIT_ProfilerEnterLeaveTailcallStub, _TEXT + jalr x0, ra, 0 +LEAF_END JIT_ProfilerEnterLeaveTailcallStub, _TEXT + +// ------------------------------------------------------------------ +#define PROFILE_ENTER 1 +#define PROFILE_LEAVE 2 +#define PROFILE_TAILCALL 4 +#define SIZEOF__PROFILE_PLATFORM_SPECIFIC_DATA 272 + +// ------------------------------------------------------------------ +.macro GenerateProfileHelper helper, flags +NESTED_ENTRY \helper\()Naked, _TEXT, NoHandler + // On entry: + // t1 = functionIDOrClientID + // t2 = profiledSp + // t6 = throwable + // + // On exit: + // Values of a0-a7, fa0-fa7, fp are preserved. + // Values of other volatile registers are not preserved. + + PROLOG_SAVE_REG_PAIR_INDEXED fp, ra, SIZEOF__PROFILE_PLATFORM_SPECIFIC_DATA // Allocate space and save Fp, Pc. + SAVE_ARGUMENT_REGISTERS sp, 16 // Save t0 and argument registers (a0-a7). + sd zero, 88(sp) // Clear functionId. + SAVE_FLOAT_ARGUMENT_REGISTERS sp, 96 // Save floating-point/SIMD registers (fa0-fa7). + addi t6, fp, SIZEOF__PROFILE_PLATFORM_SPECIFIC_DATA // Compute probeSp - initial value of Sp on entry to the helper. + sd t6, 224(sp) // Save probeSp. + sd t2, 232(sp) // Save profiledSp. + + sd zero, 240(sp) // Clear hiddenArg. + addi t6, zero, \flags + sw t6, 248(sp) // Save flags. + sw zero, 252(sp) // clear unused field. + + addi a1, t1, 0 + addi a2, sp, 0 + call C_FUNC(\helper) + + RESTORE_ARGUMENT_REGISTERS sp, 16 // Restore t0 and argument registers. + RESTORE_FLOAT_ARGUMENT_REGISTERS sp, 96 // Restore floating-point/SIMD registers. + EPILOG_RESTORE_REG_PAIR_INDEXED fp, ra, SIZEOF__PROFILE_PLATFORM_SPECIFIC_DATA + EPILOG_RETURN + +NESTED_END \helper\()Naked, _TEXT +.endm + +GenerateProfileHelper ProfileEnter, PROFILE_ENTER +GenerateProfileHelper ProfileLeave, PROFILE_LEAVE +GenerateProfileHelper ProfileTailcall, PROFILE_TAILCALL + +#endif // PROFILING_SUPPORTED + + +#ifdef FEATURE_TIERED_COMPILATION + +NESTED_ENTRY OnCallCountThresholdReachedStub, _TEXT, NoHandler + PROLOG_WITH_TRANSITION_BLOCK + + addi a0, sp, __PWTB_TransitionBlock // TransitionBlock * + addi a1, t3, 0 // stub-identifying token + call C_FUNC(OnCallCountThresholdReached) + addi t4, a0, 0 + + EPILOG_WITH_TRANSITION_BLOCK_TAILCALL + EPILOG_BRANCH_REG t4 +NESTED_END OnCallCountThresholdReachedStub, _TEXT + +#endif // FEATURE_TIERED_COMPILATION diff --git a/src/coreclr/vm/riscv64/calldescrworkerloongarch64.S b/src/coreclr/vm/riscv64/calldescrworkerloongarch64.S deleted file mode 100644 index a7cd5b6c4d2403..00000000000000 --- a/src/coreclr/vm/riscv64/calldescrworkerloongarch64.S +++ /dev/null @@ -1,7 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -#include "unixasmmacros.inc" -#include "asmconstants.h" - -#error "TODO-RISCV64: missing implementation" diff --git a/src/coreclr/vm/riscv64/calldescrworkerriscv64.S b/src/coreclr/vm/riscv64/calldescrworkerriscv64.S new file mode 100644 index 00000000000000..38f248fd632d4f --- /dev/null +++ b/src/coreclr/vm/riscv64/calldescrworkerriscv64.S @@ -0,0 +1,205 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#include "unixasmmacros.inc" +#include "asmconstants.h" + +//----------------------------------------------------------------------------- +// This helper routine enregisters the appropriate arguments and makes the +// actual call. +//----------------------------------------------------------------------------- +//void CallDescrWorkerInternal(CallDescrData * pCallDescrData); + +NESTED_ENTRY CallDescrWorkerInternal, _TEXT, NoHandler + PROLOG_SAVE_REG_PAIR_INDEXED fp, ra, 0x20 + PROLOG_SAVE_REG s1, 16 + + lwu a1, CallDescrData__numStackSlots(a0) + + addi s1, a0, 0 // save pCallDescrData in s1 + beq a1, zero, LOCAL_LABEL(donestack) + + slli a2, a1, 3 + andi a0, a2, 0x8 + sub t4, sp, a0 // padding on high-addr + add a0, a0, a2 + sub sp, sp, a0 // stack-16byte aligned + + ld a0, CallDescrData__pSrc(s1) + + add a2, a0, a2 // pSrcEnd=pSrc+8*numStackSlots + + // This loop copies numStackSlots words + // from [pSrcEnd-8,pSrcEnd-16,...] to [sp-8,sp-16,...] +LOCAL_LABEL(stackloop): + addi a2, a2, -8 + ld a4, 0(a2) + addi t4, t4, -8 + sd a4, 0(t4) + addi a1, a1, -1 + bne a1, zero, LOCAL_LABEL(stackloop) + +LOCAL_LABEL(donestack): + // If FP arguments are supplied in registers (t4 != NULL) + ld t4, CallDescrData__pFloatArgumentRegisters(s1) + beq t4, zero, LOCAL_LABEL(NoFloatingPoint) + + fld fa0, 0(t4) + fld fa1, 8(t4) + fld fa2, 16(t4) + fld fa3, 24(t4) + fld fa4, 32(t4) + fld fa5, 40(t4) + fld fa6, 48(t4) + fld fa7, 56(t4) + +LOCAL_LABEL(NoFloatingPoint): + // Copy [pArgumentRegisters, ..., pArgumentRegisters + 56] + // into a0, ..., a7 + ld t4, CallDescrData__pArgumentRegisters(s1) + ld a0, 0(t4) + ld a1, 8(t4) + ld a2, 16(t4) + ld a3, 24(t4) + ld a4, 32(t4) + ld a5, 40(t4) + ld a6, 48(t4) + ld a7, 56(t4) + + ld t4, CallDescrData__pTarget(s1) + + // call pTarget + jalr ra, 0(t4) + + lw a3, CallDescrData__fpReturnSize(s1) + + // Int return case + beq a3, zero, LOCAL_LABEL(IntReturn) + + // Struct with Float/Double field return case. + ori t4, zero, CallDescrData__flagOneFloat + beq t4, a3, LOCAL_LABEL(FloatReturn) + + ori t4, zero, CallDescrData__flagOneDouble + beq t4, a3, LOCAL_LABEL(DoubleReturn) + + ori t4, zero, CallDescrData__flagFloatInt + beq t4, a3, LOCAL_LABEL(FloatIntReturn) + + ori t4, zero, CallDescrData__flagDoubleInt + beq t4, a3, LOCAL_LABEL(DoubleIntReturn) + + ori t4, zero, CallDescrData__flagFloatLong + beq t4, a3, LOCAL_LABEL(FloatLongReturn) + + ori t4, zero, CallDescrData__flagDoubleLong + beq t4, a3, LOCAL_LABEL(DoubleLongReturn) + + ori t4, zero, CallDescrData__flagIntFloat + beq t4, a3, LOCAL_LABEL(IntFloatReturn) + + ori t4, zero, CallDescrData__flagLongFloat + beq t4, a3, LOCAL_LABEL(LongFloatReturn) + + ori t4, zero, CallDescrData__flagIntDouble + beq t4, a3, LOCAL_LABEL(IntDoubleReturn) + + ori t4, zero, CallDescrData__flagLongDouble + beq t4, a3, LOCAL_LABEL(LongDoubleReturn) + + ori t4, zero, CallDescrData__flagFloatFloat + beq t4, a3, LOCAL_LABEL(FloatFloatReturn) + + ori t4, zero, CallDescrData__flagDoubleFloat + beq t4, a3, LOCAL_LABEL(DoubleFloatReturn) + + ori t4, zero, CallDescrData__flagFloatDouble + beq t4, a3, LOCAL_LABEL(FloatDoubleReturn) + + ori t4, zero, CallDescrData__flagDoubleDouble + beq t4, a3, LOCAL_LABEL(DoubleDoubleReturn) + +LOCAL_LABEL(NotCorrectReturn): + sw ra, 0(zero) + EMIT_BREAKPOINT // Unreachable + +LOCAL_LABEL(FloatReturn): + fsw f0, CallDescrData__returnValue(s1) + jal x0, LOCAL_LABEL(ReturnDone) + +LOCAL_LABEL(DoubleReturn): + fsd fa0, CallDescrData__returnValue(s1) + jal x0, LOCAL_LABEL(ReturnDone) + +LOCAL_LABEL(FloatIntReturn): + fsw fa0, CallDescrData__returnValue(s1) + sw a0, (CallDescrData__returnValue + 4)(s1) + jal x0, LOCAL_LABEL(ReturnDone) + +LOCAL_LABEL(DoubleIntReturn): + fsd fa0, CallDescrData__returnValue(s1) + sw a0, (CallDescrData__returnValue + 8)(s1) + jal x0, LOCAL_LABEL(ReturnDone) + +LOCAL_LABEL(FloatLongReturn): + fsw fa0, CallDescrData__returnValue(s1) + sd a0, (CallDescrData__returnValue + 8)(s1) + jal x0, LOCAL_LABEL(ReturnDone) + +LOCAL_LABEL(DoubleLongReturn): + fsd fa0, CallDescrData__returnValue(s1) + sd a0, (CallDescrData__returnValue + 8)(s1) + jal x0, LOCAL_LABEL(ReturnDone) + +LOCAL_LABEL(IntFloatReturn): + sw a0, CallDescrData__returnValue(s1) + fsw fa0, (CallDescrData__returnValue + 4)(s1) + jal x0, LOCAL_LABEL(ReturnDone) + +LOCAL_LABEL(LongFloatReturn): + sd a0, CallDescrData__returnValue(s1) + fsw fa0, (CallDescrData__returnValue + 8)(s1) + jal x0, LOCAL_LABEL(ReturnDone) + +LOCAL_LABEL(IntDoubleReturn): + sw a0, CallDescrData__returnValue(s1) + fsd fa0, (CallDescrData__returnValue + 8)(s1) + jal x0, LOCAL_LABEL(ReturnDone) + +LOCAL_LABEL(LongDoubleReturn): + sd a0, CallDescrData__returnValue(s1) + fsd fa0, (CallDescrData__returnValue + 8)(s1) + jal x0, LOCAL_LABEL(ReturnDone) + +LOCAL_LABEL(FloatFloatReturn): + fsw fa0, CallDescrData__returnValue(s1) + fsw fa1, (CallDescrData__returnValue + 4)(s1) + jal x0, LOCAL_LABEL(ReturnDone) + +LOCAL_LABEL(DoubleFloatReturn): + fsd fa0, CallDescrData__returnValue(s1) + fsw fa1, (CallDescrData__returnValue + 8)(s1) + jal x0, LOCAL_LABEL(ReturnDone) + +LOCAL_LABEL(FloatDoubleReturn): + fsw fa0, CallDescrData__returnValue(s1) + fsd fa1, (CallDescrData__returnValue + 8)(s1) + jal x0, LOCAL_LABEL(ReturnDone) + +LOCAL_LABEL(DoubleDoubleReturn): + fsd fa0, CallDescrData__returnValue(s1) + fsd fa1, (CallDescrData__returnValue + 8)(s1) + jal x0, LOCAL_LABEL(ReturnDone) + +LOCAL_LABEL(IntReturn): + // Save return value into retbuf for int + sd a0, CallDescrData__returnValue(s1) + sd a1, (CallDescrData__returnValue + 8)(s1) + +LOCAL_LABEL(ReturnDone): + + EPILOG_STACK_RESTORE + EPILOG_RESTORE_REG s1, 16 + EPILOG_RESTORE_REG_PAIR_INDEXED fp, ra, 0x20 + jalr x0, ra, 0 +NESTED_END CallDescrWorkerInternal, _TEXT diff --git a/src/coreclr/vm/riscv64/cgencpu.h b/src/coreclr/vm/riscv64/cgencpu.h index 9a0cdd4e406d37..41e2cae04f65aa 100644 --- a/src/coreclr/vm/riscv64/cgencpu.h +++ b/src/coreclr/vm/riscv64/cgencpu.h @@ -1,4 +1,503 @@ -// Licensed to the .NET Foundation under one or more agreements. + // The .NET Foundation licenses this file to you under the MIT license. -#error "TODO-RISCV64: missing implementation" +#ifndef TARGET_RISCV64 +#error Should only include "cGenCpu.h" for RISCV64 builds +#endif + +#ifndef __cgencpu_h__ +#define __cgencpu_h__ + +#define INSTRFMT_K64 +#include + +#ifndef TARGET_UNIX +#define USE_REDIRECT_FOR_GCSTRESS +#endif // TARGET_UNIX + +EXTERN_C void getFPReturn(int fpSize, INT64 *pRetVal); +EXTERN_C void setFPReturn(int fpSize, INT64 retVal); + + +class ComCallMethodDesc; + +extern PCODE GetPreStubEntryPoint(); + +#define COMMETHOD_PREPAD 24 // # extra bytes to allocate in addition to sizeof(ComCallMethodDesc) +#ifdef FEATURE_COMINTEROP +#define COMMETHOD_CALL_PRESTUB_SIZE 24 +#define COMMETHOD_CALL_PRESTUB_ADDRESS_OFFSET 16 // the offset of the call target address inside the prestub +#endif // FEATURE_COMINTEROP + +#define STACK_ALIGN_SIZE 16 + +#define JUMP_ALLOCATE_SIZE 16 // # bytes to allocate for a jump instruction +#define BACK_TO_BACK_JUMP_ALLOCATE_SIZE 16 // # bytes to allocate for a back to back jump instruction + +#define HAS_NDIRECT_IMPORT_PRECODE 1 + +#define USE_INDIRECT_CODEHEADER + +#define HAS_FIXUP_PRECODE 1 + +// ThisPtrRetBufPrecode one is necessary for closed delegates over static methods with return buffer +#define HAS_THISPTR_RETBUF_PRECODE 1 + +#define CODE_SIZE_ALIGN 8 +#define CACHE_LINE_SIZE 64 +#define LOG2SLOT LOG2_PTRSIZE + +#define ENREGISTERED_RETURNTYPE_MAXSIZE 16 // bytes (two FP registers: f10 and f11 +#define ENREGISTERED_RETURNTYPE_INTEGER_MAXSIZE 16 // bytes (two int registers: a0 and a1) +#define ENREGISTERED_PARAMTYPE_MAXSIZE 16 // bytes (max value type size that can be passed by value) + +#define CALLDESCR_ARGREGS 1 // CallDescrWorker has ArgumentRegister parameter +#define CALLDESCR_FPARGREGS 1 // CallDescrWorker has FloatArgumentRegisters parameter +// #define CALLDESCR_RETBUFFARGREG 1 // CallDescrWorker has RetBuffArg parameter that's separate from arg regs // TODO RISCV64 + +#define FLOAT_REGISTER_SIZE 16 // each register in FloatArgumentRegisters is 16 bytes. + +// Given a return address retrieved during stackwalk, +// this is the offset by which it should be decremented to arrive at the callsite. +#define STACKWALK_CONTROLPC_ADJUST_OFFSET 4 + +//********************************************************************** +// Parameter size +//********************************************************************** + +inline unsigned StackElemSize(unsigned parmSize, bool isValueType, bool isFloatHfa) +{ + const unsigned stackSlotSize = 8; + return ALIGN_UP(parmSize, stackSlotSize); +} + +// +// JIT HELPERS. +// +// Create alias for optimized implementations of helpers provided on this platform +// +#define JIT_GetSharedGCStaticBase JIT_GetSharedGCStaticBase_SingleAppDomain +#define JIT_GetSharedNonGCStaticBase JIT_GetSharedNonGCStaticBase_SingleAppDomain +#define JIT_GetSharedGCStaticBaseNoCtor JIT_GetSharedGCStaticBaseNoCtor_SingleAppDomain +#define JIT_GetSharedNonGCStaticBaseNoCtor JIT_GetSharedNonGCStaticBaseNoCtor_SingleAppDomain + +//********************************************************************** +// Frames +//********************************************************************** + +//-------------------------------------------------------------------- +// This represents the callee saved (non-volatile) integer registers saved as +// of a FramedMethodFrame. +//-------------------------------------------------------------------- +typedef DPTR(struct CalleeSavedRegisters) PTR_CalleeSavedRegisters; +struct CalleeSavedRegisters { + INT64 sp; // stack pointer + INT64 fp; // frame pointer + INT64 s1, s2, s3, s4, s5, s6, s7, s8, s9, s10, s11; + INT64 tp, gp; +}; + +//-------------------------------------------------------------------- +// This represents the arguments that are stored in volatile integer registers. +// This should not overlap the CalleeSavedRegisters since those are already +// saved separately and it would be wasteful to save the same register twice. +// If we do use a non-volatile register as an argument, then the ArgIterator +// will probably have to communicate this back to the PromoteCallerStack +// routine to avoid a double promotion. +//-------------------------------------------------------------------- +#define NUM_ARGUMENT_REGISTERS 8 +typedef DPTR(struct ArgumentRegisters) PTR_ArgumentRegisters; +struct ArgumentRegisters { + INT64 a[8]; // a0 ....a7 +}; + +#define ARGUMENTREGISTERS_SIZE sizeof(ArgumentRegisters) + + +//-------------------------------------------------------------------- +// This represents the floating point argument registers which are saved +// as part of the NegInfo for a FramedMethodFrame. Note that these +// might not be saved by all stubs: typically only those that call into +// C++ helpers will need to preserve the values in these volatile +// registers. +//-------------------------------------------------------------------- +#define NUM_FLOAT_ARGUMENT_REGISTERS 8 +typedef DPTR(struct FloatArgumentRegisters) PTR_FloatArgumentRegisters; +struct FloatArgumentRegisters { + //TODO: not supports RISCV64-SIMD. + double f[8]; // f0-f7 +}; + + +//********************************************************************** +// Exception handling +//********************************************************************** + +inline PCODE GetIP(const T_CONTEXT * context) { + LIMITED_METHOD_DAC_CONTRACT; + return context->Pc; +} + +inline void SetIP(T_CONTEXT *context, PCODE ip) { + LIMITED_METHOD_DAC_CONTRACT; + context->Pc = ip; +} + +inline TADDR GetSP(const T_CONTEXT * context) { + LIMITED_METHOD_DAC_CONTRACT; + return TADDR(context->Sp); +} + +inline TADDR GetRA(const T_CONTEXT * context) { + LIMITED_METHOD_DAC_CONTRACT; + return context->Ra; +} + +inline void SetRA( T_CONTEXT * context, TADDR ip) { + LIMITED_METHOD_DAC_CONTRACT; + context->Ra = ip; +} + +inline TADDR GetReg(T_CONTEXT * context, int Regnum) +{ + LIMITED_METHOD_DAC_CONTRACT; + _ASSERTE(Regnum >= 0 && Regnum < 32 ); + return (TADDR)(&context->R0 + Regnum); +} + +inline void SetReg(T_CONTEXT * context, int Regnum, PCODE RegContent) +{ + LIMITED_METHOD_DAC_CONTRACT; + _ASSERTE(Regnum >= 0 && Regnum <=28 ); + *(&context->R0 + Regnum) = RegContent; +} + +extern "C" LPVOID __stdcall GetCurrentSP(); + +inline void SetSP(T_CONTEXT *context, TADDR sp) { + LIMITED_METHOD_DAC_CONTRACT; + context->Sp = DWORD64(sp); +} + +inline void SetFP(T_CONTEXT *context, TADDR fp) { + LIMITED_METHOD_DAC_CONTRACT; + context->Fp = DWORD64(fp); +} + +inline TADDR GetFP(const T_CONTEXT * context) +{ + LIMITED_METHOD_DAC_CONTRACT; + return (TADDR)(context->Fp); +} + + +inline TADDR GetMem(PCODE address, SIZE_T size, bool signExtend) +{ + TADDR mem; + LIMITED_METHOD_DAC_CONTRACT; + EX_TRY + { + switch (size) + { + case 4: + if (signExtend) + mem = *(int32_t*)address; + else + mem = *(uint32_t*)address; + break; + case 8: + mem = *(uint64_t*)address; + break; + default: + UNREACHABLE(); + } + } + EX_CATCH + { + mem = NULL; + _ASSERTE(!"Memory read within jitted Code Failed, this should not happen!!!!"); + } + EX_END_CATCH(SwallowAllExceptions); + return mem; +} + +#ifdef FEATURE_COMINTEROP +void emitCOMStubCall (ComCallMethodDesc *pCOMMethodRX, ComCallMethodDesc *pCOMMethodRW, PCODE target); +#endif // FEATURE_COMINTEROP + +inline BOOL ClrFlushInstructionCache(LPCVOID pCodeAddr, size_t sizeOfCode, bool hasCodeExecutedBefore = false) +{ + return FlushInstructionCache(GetCurrentProcess(), pCodeAddr, sizeOfCode); +} + +//------------------------------------------------------------------------ +inline void emitJump(LPBYTE pBufferRX, LPBYTE pBufferRW, LPVOID target) +{ + LIMITED_METHOD_CONTRACT; + UINT32* pCode = (UINT32*)pBufferRW; + + // We require 8-byte alignment so the LD instruction is aligned properly + _ASSERTE(((UINT_PTR)pCode & 7) == 0); + + // auipc ra, 0 + // ld ra, ra, 16 + // jalr r0, ra, 0 + // nop //padding. + + pCode[0] = 0x00000097; // auipc ra, 0 + pCode[1] = 0x0100b083; // ld ra, 16(ra) + pCode[2] = 0x00008067; // jalr x0, ra, 0 + pCode[3] = 0x00000013; // padding nop. Also used for isJump. + + // Ensure that the updated instructions get updated in the I-Cache + ClrFlushInstructionCache(pBufferRX, 16); + + *((LPVOID *)(pCode + 4)) = target; // 64-bit target address +} + +//------------------------------------------------------------------------ +// Given the same pBuffer that was used by emitJump this method +// decodes the instructions and returns the jump target +inline PCODE decodeJump(PCODE pCode) +{ + LIMITED_METHOD_CONTRACT; + + TADDR pInstr = PCODEToPINSTR(pCode); + + return *dac_cast(pInstr + 2*sizeof(DWORD)); +} + +//------------------------------------------------------------------------ +inline BOOL isJump(PCODE pCode) +{ + LIMITED_METHOD_DAC_CONTRACT; + + TADDR pInstr = PCODEToPINSTR(pCode); + + return *dac_cast(pInstr) == 0x58000050; +} + +//------------------------------------------------------------------------ +inline BOOL isBackToBackJump(PCODE pBuffer) +{ + WRAPPER_NO_CONTRACT; + SUPPORTS_DAC; + return isJump(pBuffer); +} + +//------------------------------------------------------------------------ +inline void emitBackToBackJump(LPBYTE pBufferRX, LPBYTE pBufferRW, LPVOID target) +{ + WRAPPER_NO_CONTRACT; + emitJump(pBufferRX, pBufferRW, target); +} + +//------------------------------------------------------------------------ +inline PCODE decodeBackToBackJump(PCODE pBuffer) +{ + WRAPPER_NO_CONTRACT; + return decodeJump(pBuffer); +} + +//---------------------------------------------------------------------- + +struct IntReg +{ + int reg; + IntReg(int reg):reg(reg) + { + _ASSERTE(0 <= reg && reg < 32); + } + + operator int () { return reg; } + operator int () const { return reg; } + int operator == (IntReg other) { return reg == other.reg; } + int operator != (IntReg other) { return reg != other.reg; } + WORD Mask() const { return 1 << reg; } +}; + +struct FloatReg +{ + int reg; + FloatReg(int reg):reg(reg) + { + _ASSERTE(0 <= reg && reg < 32); + } + + operator int () { return reg; } + operator int () const { return reg; } + int operator == (FloatReg other) { return reg == other.reg; } + int operator != (FloatReg other) { return reg != other.reg; } + WORD Mask() const { return 1 << reg; } +}; + +struct CondCode +{ + int cond; + CondCode(int cond):cond(cond) + { + _ASSERTE(0 <= cond && cond < 16); + } +}; + +const IntReg RegSp = IntReg(2); +const IntReg RegFp = IntReg(8); +const IntReg RegRa = IntReg(1); + +#define GetEEFuncEntryPoint(pfn) GFN_TADDR(pfn) + +#if 1 // TODO RISCV64 +class StubLinkerCPU : public StubLinker +{ + +public: + + // BitFlags for EmitLoadStoreReg(Pair)Imm methods + enum { + eSTORE = 0x0, + eLOAD = 0x1, + }; + + static void Init(); + static bool isValidSimm12(int value) { + return -( ((int)1) << 11 ) <= value && value < ( ((int)1) << 11 ); + } + + void EmitCallManagedMethod(MethodDesc *pMD, BOOL fTailCall); + void EmitCallLabel(CodeLabel *target, BOOL fTailCall, BOOL fIndirect); + + void EmitShuffleThunk(struct ShuffleEntry *pShuffleEntryArray); + +#if defined(FEATURE_SHARE_GENERIC_CODE) + void EmitComputedInstantiatingMethodStub(MethodDesc* pSharedMD, struct ShuffleEntry *pShuffleEntryArray, void* extraArg); +#endif // FEATURE_SHARE_GENERIC_CODE + +#ifdef _DEBUG + void EmitNop() { _ASSERTE(!"RISCV64: not implementation on riscv64!!!"); } +#endif + void EmitBreakPoint() { _ASSERTE(!"RISCV64: not implementation on riscv64!!!"); } + void EmitMovConstant(IntReg target, UINT64 constant); + void EmitCmpImm(IntReg reg, int imm); + void EmitCmpReg(IntReg Xn, IntReg Xm); + void EmitCondFlagJump(CodeLabel * target, UINT cond); + void EmitJumpRegister(IntReg regTarget); + void EmitMovReg(IntReg dest, IntReg source); + + void EmitSubImm(IntReg Xd, IntReg Xn, unsigned int value); + void EmitAddImm(IntReg Xd, IntReg Xn, unsigned int value); + + void EmitLoadStoreRegPairImm(DWORD flags, IntReg Xt1, IntReg Xt2, IntReg Xn, int offset=0); + void EmitLoadStoreRegPairImm(DWORD flags, FloatReg Ft1, FloatReg Ft2, IntReg Xn, int offset=0); + + void EmitLoadStoreRegImm(DWORD flags, IntReg Xt, IntReg Xn, int offset=0); + void EmitLoadStoreRegImm(DWORD flags, FloatReg Ft, IntReg Xn, int offset=0); + + void EmitLoadFloatRegImm(FloatReg ft, IntReg base, int offset); + + void EmitCallRegister(IntReg reg); + void EmitRet(IntReg reg); +}; +#endif + +extern "C" void SinglecastDelegateInvokeStub(); + + +// preferred alignment for data +#define DATA_ALIGNMENT 8 + +// TODO RISCV64 +struct DECLSPEC_ALIGN(16) UMEntryThunkCode +{ + DWORD m_code[4]; + + TADDR m_pTargetCode; + TADDR m_pvSecretParam; + + void Encode(UMEntryThunkCode *pEntryThunkCodeRX, BYTE* pTargetCode, void* pvSecretParam); + void Poison(); + + LPCBYTE GetEntryPoint() const + { + LIMITED_METHOD_CONTRACT; + + return (LPCBYTE)this; + } + + static int GetEntryPointOffset() + { + LIMITED_METHOD_CONTRACT; + + return 0; + } +}; + +struct HijackArgs +{ + union + { + struct { + DWORD64 A0; + DWORD64 A1; + }; + size_t ReturnValue[2]; + }; + union + { + struct { + DWORD64 FA0; + DWORD64 FA1; + }; + size_t FPReturnValue[2]; + }; + DWORD64 Fp; // frame pointer + DWORD64 Gp, Tp, S1, S2, S3, S4, S5, S6, S7, S8, S9, S10, S11; + union + { + DWORD64 Ra; + size_t ReturnAddress; + }; + }; + +// Precode to shuffle this and retbuf for closed delegates over static methods with return buffer +struct ThisPtrRetBufPrecode { + + static const int Type = 0x2; + + UINT32 m_rgCode[6]; + TADDR m_pTarget; + TADDR m_pMethodDesc; + + void Init(MethodDesc* pMD, LoaderAllocator *pLoaderAllocator); + + TADDR GetMethodDesc() + { + LIMITED_METHOD_DAC_CONTRACT; + + return m_pMethodDesc; + } + + PCODE GetTarget() + { + LIMITED_METHOD_DAC_CONTRACT; + return m_pTarget; + } + +#ifndef DACCESS_COMPILE + BOOL SetTargetInterlocked(TADDR target, TADDR expected) + { + CONTRACTL + { + THROWS; + GC_NOTRIGGER; + } + CONTRACTL_END; + + ExecutableWriterHolder precodeWriterHolder(this, sizeof(ThisPtrRetBufPrecode)); + return (TADDR)InterlockedCompareExchange64( + (LONGLONG*)&precodeWriterHolder.GetRW()->m_pTarget, (TADDR)target, (TADDR)expected) == expected; + } +#endif // !DACCESS_COMPILE +}; +typedef DPTR(ThisPtrRetBufPrecode) PTR_ThisPtrRetBufPrecode; + +#endif // __cgencpu_h__ diff --git a/src/coreclr/vm/riscv64/crthelpers.S b/src/coreclr/vm/riscv64/crthelpers.S index f6c1fb2c96ce46..d4152a96c77015 100644 --- a/src/coreclr/vm/riscv64/crthelpers.S +++ b/src/coreclr/vm/riscv64/crthelpers.S @@ -3,4 +3,34 @@ #include "unixasmmacros.inc" -#error "TODO-RISCV64: missing implementation" +// JIT_MemSet/JIT_MemCpy +// +// It is IMPORTANT that the exception handling code is able to find these guys +// on the stack, but on non-windows platforms we can just defer to the platform +// implementation. +// +LEAF_ENTRY JIT_MemSet, _TEXT + beq a2, zero, LOCAL_LABEL(JIT_MemSet_ret) + + lb zero, 0(a0) // Is this really needed ? + + tail memset + +LOCAL_LABEL(JIT_MemSet_ret): + jalr x0, 0(ra) +LEAF_END_MARKED JIT_MemSet, _TEXT + +////NOTE: Here must use LEAF_END_MARKED! not LEAF_END !!! +LEAF_ENTRY JIT_MemCpy, _TEXT + beq a2, zero, LOCAL_LABEL(JIT_MemCpy_ret) + + lb zero, 0(a0) + lb zero, 0(a1) // Is this really needed ? + + tail memcpy + +LOCAL_LABEL(JIT_MemCpy_ret): + jalr x0, 0(ra) + +////NOTE: Here must use LEAF_END_MARKED! not LEAF_END !!! +LEAF_END_MARKED JIT_MemCpy, _TEXT diff --git a/src/coreclr/vm/riscv64/excepcpu.h b/src/coreclr/vm/riscv64/excepcpu.h index 4800154434a53a..eb575235af8feb 100644 --- a/src/coreclr/vm/riscv64/excepcpu.h +++ b/src/coreclr/vm/riscv64/excepcpu.h @@ -1,7 +1,50 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +// +// + #ifndef __excepcpu_h__ #define __excepcpu_h__ -#error "TODO-RISCV64: missing implementation" + +#define THROW_CONTROL_FOR_THREAD_FUNCTION RedirectForThreadAbort +EXTERN_C void RedirectForThreadAbort(); + + +#define STATUS_CLR_GCCOVER_CODE STATUS_ILLEGAL_INSTRUCTION + +class Thread; +class FaultingExceptionFrame; + +#define INSTALL_EXCEPTION_HANDLING_RECORD(record) +#define UNINSTALL_EXCEPTION_HANDLING_RECORD(record) +// +// On ARM, the COMPlusFrameHandler's work is done by our personality routine. +// +#define DECLARE_CPFH_EH_RECORD(pCurThread) + +// +// Retrieves the redirected CONTEXT* from the stack frame of one of the +// RedirectedHandledJITCaseForXXX_Stub's. +// +PTR_CONTEXT GetCONTEXTFromRedirectedStubStackFrame(T_DISPATCHER_CONTEXT * pDispatcherContext); +PTR_CONTEXT GetCONTEXTFromRedirectedStubStackFrame(T_CONTEXT * pContext); + +// +// Retrieves the FaultingExceptionFrame* from the stack frame of +// RedirectForThrowControl. +// +FaultingExceptionFrame *GetFrameFromRedirectedStubStackFrame (T_DISPATCHER_CONTEXT *pDispatcherContext); + +inline +PCODE GetAdjustedCallAddress(PCODE returnAddress) +{ + LIMITED_METHOD_CONTRACT; + + return returnAddress - 4; +} + +BOOL AdjustContextForVirtualStub(EXCEPTION_RECORD *pExceptionRecord, T_CONTEXT *pContext); + +#endif // __excepcpu_h__ diff --git a/src/coreclr/vm/riscv64/gmscpu.h b/src/coreclr/vm/riscv64/gmscpu.h index 9a0cdd4e406d37..6506b10b8f751c 100644 --- a/src/coreclr/vm/riscv64/gmscpu.h +++ b/src/coreclr/vm/riscv64/gmscpu.h @@ -1,4 +1,102 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -#error "TODO-RISCV64: missing implementation" +/**************************************************************/ +/* gmscpu.h */ +/**************************************************************/ +/* HelperFrame is defines 'GET_STATE(machState)' macro, which + figures out what the state of the machine will be when the + current method returns. It then stores the state in the + JIT_machState structure. */ + +/**************************************************************/ + +#ifndef __gmscpu_h__ +#define __gmscpu_h__ + +#define __gmscpu_h__ + +// CalleeSaveRegisters +#define NUM_NONVOLATILE_CONTEXT_POINTERS 14 + +struct MachState { + ULONG64 captureCalleeSavedRegisters[NUM_NONVOLATILE_CONTEXT_POINTERS]; // preserved registers + PTR_ULONG64 ptrCalleeSavedRegisters[NUM_NONVOLATILE_CONTEXT_POINTERS]; // pointers to preserved registers + TADDR _pc; // program counter after the function returns + TADDR _sp; // stack pointer after the function returns + BOOL _isValid; + + BOOL isValid() { LIMITED_METHOD_DAC_CONTRACT; return _isValid; } + TADDR GetRetAddr() { LIMITED_METHOD_DAC_CONTRACT; return _pc; } +}; + +struct LazyMachState : public MachState{ + + TADDR captureSp; // Stack pointer at the time of capture + TADDR captureIp; // Instruction pointer at the time of capture + + void setLazyStateFromUnwind(MachState* copy); + static void unwindLazyState(LazyMachState* baseState, + MachState* lazyState, + DWORD threadId, + int funCallDepth = 1, + HostCallPreference hostCallPreference = AllowHostCalls); +}; + +inline void LazyMachState::setLazyStateFromUnwind(MachState* copy) +{ +#if defined(DACCESS_COMPILE) + // This function cannot be called in DAC because DAC cannot update target memory. + DacError(E_FAIL); + return; + +#else // !DACCESS_COMPILE + + _sp = copy->_sp; + _pc = copy->_pc; + + // Capture* has already been set, so there is no need to touch it + + // loop over the nonvolatile context pointers and make + // sure to properly copy interior pointers into the + // new struct + + PULONG64* pSrc = (PULONG64 *)©->ptrCalleeSavedRegisters; + PULONG64* pDst = (PULONG64 *)&this->ptrCalleeSavedRegisters; + + const PULONG64 LowerBoundDst = (PULONG64) this; + const PULONG64 LowerBoundSrc = (PULONG64) copy; + + const PULONG64 UpperBoundSrc = (PULONG64) ((BYTE*)LowerBoundSrc + sizeof(*copy)); + + for (int i = 0; i < NUM_NONVOLATILE_CONTEXT_POINTERS; i++) + { + PULONG64 valueSrc = *pSrc++; + + if ((LowerBoundSrc <= valueSrc) && (valueSrc < UpperBoundSrc)) + { + // make any pointer interior to 'src' interior to 'dst' + valueSrc = (PULONG64)((BYTE*)valueSrc - (BYTE*)LowerBoundSrc + (BYTE*)LowerBoundDst); + } + + *pDst++ = valueSrc; + captureCalleeSavedRegisters[i] = copy->captureCalleeSavedRegisters[i]; + } + + + // this has to be last because we depend on write ordering to + // synchronize the race implicit in updating this struct + VolatileStore(&_isValid, TRUE); +#endif // DACCESS_COMPILE +} + +// Do the initial capture of the machine state. This is meant to be +// as light weight as possible, as we may never need the state that +// we capture. +EXTERN_C void LazyMachStateCaptureState(struct LazyMachState *pState); + +#define CAPTURE_STATE(machState, ret) \ + LazyMachStateCaptureState(machState) + + +#endif diff --git a/src/coreclr/vm/riscv64/pinvokestubs.S b/src/coreclr/vm/riscv64/pinvokestubs.S index 3515f38c8120d7..ea5d245c56a0d6 100644 --- a/src/coreclr/vm/riscv64/pinvokestubs.S +++ b/src/coreclr/vm/riscv64/pinvokestubs.S @@ -4,4 +4,185 @@ #include "asmconstants.h" #include "unixasmmacros.inc" -#error "TODO-RISCV64: missing implementation" +// ------------------------------------------------------------------ +// Macro to generate PInvoke Stubs. +// __PInvokeStubFuncName : function which calls the actual stub obtained from VASigCookie +// __PInvokeGenStubFuncName : function which generates the IL stubs for PInvoke +// +// Params :- +// FuncPrefix : prefix of the function name for the stub +// Eg. VarargPinvoke, GenericPInvokeCalli +// VASigCookieReg : register which contains the VASigCookie +// SaveFPArgs : "Yes" or "No" . For varidic functions FP Args are not present in FP regs +// So need not save FP Args registers for vararg Pinvoke +.macro PINVOKE_STUB __PInvokeStubFuncName,__PInvokeGenStubFuncName,__PInvokeStubWorkerName,VASigCookieReg,HiddenArg,SaveFPArgs,ShiftLeftAndOrSecret=0 + + + NESTED_ENTRY \__PInvokeStubFuncName, _TEXT, NoHandler + + // get the stub + ld t0, (VASigCookie__pNDirectILStub)(\VASigCookieReg) + + // if null goto stub generation + beq t0, zero, \__PInvokeGenStubFuncName + + .if (\ShiftLeftAndOrSecret == 1) + // + // We need to distinguish between a MethodDesc* and an unmanaged target. + // The way we do this is to shift the managed target to the left by one bit and then set the + // least significant bit to 1. This works because MethodDesc* are always 8-byte aligned. + // + slli \HiddenArg, \HiddenArg, 1 + ori \HiddenArg, \HiddenArg, 1 + .endif + + jalr x0, t0, 0 + NESTED_END \__PInvokeStubFuncName, _TEXT + + NESTED_ENTRY \__PInvokeGenStubFuncName, _TEXT, NoHandler + + PROLOG_WITH_TRANSITION_BLOCK 0, 0, \SaveFPArgs + + // a2 = Umanaged Target\MethodDesc + addi a2, \HiddenArg, 0 + + // a1 = VaSigCookie + addi a1, \VASigCookieReg, 0 + + // a0 = pTransitionBlock + addi a0, sp, __PWTB_TransitionBlock + + // save hidden arg + addi s1, \HiddenArg, 0 + + // save VASigCookieReg + addi s2, \VASigCookieReg, 0 + + call C_FUNC(\__PInvokeStubWorkerName) + + // restore VASigCookieReg + addi \VASigCookieReg, s2, 0 + + // restore hidden arg (method desc or unmanaged target) + addi \HiddenArg, s1, 0 + + EPILOG_WITH_TRANSITION_BLOCK_TAILCALL + + EPILOG_BRANCH C_FUNC(\__PInvokeStubFuncName) + NESTED_END \__PInvokeGenStubFuncName, _TEXT +.endm + +// ------------------------------------------------------------------ +// IN: +// InlinedCallFrame (x0) = pointer to the InlinedCallFrame data, including the GS cookie slot (GS cookie right +// before actual InlinedCallFrame data) +// +// + NESTED_ENTRY JIT_PInvokeBegin, _TEXT, NoHandler + PROLOG_SAVE_REG_PAIR_INDEXED fp, ra, 32 + PROLOG_SAVE_REG s1, 16 // the stack slot at sp+24 is empty for 16 byte alignment + + PREPARE_EXTERNAL_VAR s_gsCookie, t0 + ld t4, 0(t0) + sd t4, 0(a0) + addi s1, a0, SIZEOF__GSCookie + + // s1 = pFrame + // set first slot to the value of InlinedCallFrame::`vftable' (checked by runtime code) + PREPARE_EXTERNAL_VAR _ZTV16InlinedCallFrame, t0 + addi t4, t0, 16 + sd t4, 0(s1) + + sd zero, (InlinedCallFrame__m_Datum)(s1) + + addi t0, sp, 32 + sd t0, (InlinedCallFrame__m_pCallSiteSP)(s1) + sd ra, (InlinedCallFrame__m_pCallerReturnAddress)(s1) + + ld t4, 0(sp) + sd t4, (InlinedCallFrame__m_pCalleeSavedFP)(s1) + + // v0 = GetThread() + call GetThreadHelper + + sd a0, (InlinedCallFrame__m_pThread)(s1) + + // pFrame->m_Next = pThread->m_pFrame; + ld t4, Thread_m_pFrame(a0) + sd t4, Frame__m_Next(s1) + + // pThread->m_pFrame = pFrame; + sd s1, (Thread_m_pFrame)(a0) + + // pThread->m_fPreemptiveGCDisabled = 0 + sw zero, (Thread_m_fPreemptiveGCDisabled)(a0) + + EPILOG_RESTORE_REG s1, 16 //the stack slot at sp+24 is empty for 16 byte alignment + EPILOG_RESTORE_REG_PAIR_INDEXED fp, ra, 32 + EPILOG_RETURN + + NESTED_END JIT_PInvokeBegin, _TEXT + +// ------------------------------------------------------------------ +// IN: +// InlinedCallFrame (x0) = pointer to the InlinedCallFrame data, including the GS cookie slot (GS cookie right +// before actual InlinedCallFrame data) +// +// + LEAF_ENTRY JIT_PInvokeEnd, _TEXT + + addi a0, a0, SIZEOF__GSCookie + ld a1, (InlinedCallFrame__m_pThread)(a0) + // a0 = pFrame + // a1 = pThread + + // pThread->m_fPreemptiveGCDisabled = 1 + ori t4, x0, 1 + sw t4, (Thread_m_fPreemptiveGCDisabled)(a1) + + // Check return trap + PREPARE_EXTERNAL_VAR g_TrapReturningThreads, t0 + lw t4, 0(t0) + bne t4, zero, LOCAL_LABEL(RarePath) + + // pThread->m_pFrame = pFrame->m_Next + ld t4, (Frame__m_Next)(a0) + sd t4, (Thread_m_pFrame)(a1) + + jalr x0, ra, 0 + +LOCAL_LABEL(RarePath): + tail JIT_PInvokeEndRarePath + +LEAF_END JIT_PInvokeEnd, _TEXT + +// ------------------------------------------------------------------ +// VarargPInvokeStub & VarargPInvokeGenILStub +// There is a separate stub when the method has a hidden return buffer arg. +// +// in: +// a0 = VASigCookie* +// t2 = MethodDesc * +// +PINVOKE_STUB VarargPInvokeStub, VarargPInvokeGenILStub, VarargPInvokeStubWorker, a0, t2, 0 + + +// ------------------------------------------------------------------ +// GenericPInvokeCalliHelper & GenericPInvokeCalliGenILStub +// Helper for generic pinvoke calli instruction +// +// in: +// t3 = VASigCookie* +// t2 = Unmanaged target +// +PINVOKE_STUB GenericPInvokeCalliHelper, GenericPInvokeCalliGenILStub, GenericPInvokeCalliStubWorker, t3, t2, 1, 1 + +//// ------------------------------------------------------------------ +//// VarargPInvokeStub_RetBuffArg & VarargPInvokeGenILStub_RetBuffArg +//// Vararg PInvoke Stub when the method has a hidden return buffer arg +//// +//// in: +//// a1 = VASigCookie* //not used ??? +//// t2 = MethodDesc* +//// +//PINVOKE_STUB VarargPInvokeStub_RetBuffArg, VarargPInvokeGenILStub_RetBuffArg, VarargPInvokeStubWorker, a1, t8, 0 diff --git a/src/coreclr/vm/riscv64/profiler.cpp b/src/coreclr/vm/riscv64/profiler.cpp index 8d7dc92a1a59e7..fc0e224b65a427 100644 --- a/src/coreclr/vm/riscv64/profiler.cpp +++ b/src/coreclr/vm/riscv64/profiler.cpp @@ -3,4 +3,309 @@ #include "common.h" -#error "TODO-RISCV64: missing implementation" +#ifdef PROFILING_SUPPORTED +#include "proftoeeinterfaceimpl.h" + +#define PROFILE_ENTER 1 +#define PROFILE_LEAVE 2 +#define PROFILE_TAILCALL 4 + +// Scratch space to store HFA return values (max 16 bytes) +#define PROFILE_PLATFORM_SPECIFIC_DATA_BUFFER_SIZE 16 + +typedef struct _PROFILE_PLATFORM_SPECIFIC_DATA +{ + void* Fp; + void* Pc; + void* x8; + ArgumentRegisters argumentRegisters; + FunctionID functionId; + FloatArgumentRegisters floatArgumentRegisters; + void* probeSp; + void* profiledSp; + void* hiddenArg; + UINT32 flags; + UINT32 unused; + BYTE buffer[PROFILE_PLATFORM_SPECIFIC_DATA_BUFFER_SIZE]; +} PROFILE_PLATFORM_SPECIFIC_DATA, *PPROFILE_PLATFORM_SPECIFIC_DATA; + +UINT_PTR ProfileGetIPFromPlatformSpecificHandle(void* pPlatformSpecificHandle) +{ + _ASSERTE(!"TODO RISCV64 NYI"); + LIMITED_METHOD_CONTRACT; + + PROFILE_PLATFORM_SPECIFIC_DATA* pData = reinterpret_cast(pPlatformSpecificHandle); + return (UINT_PTR)pData->Pc; +} + +void ProfileSetFunctionIDInPlatformSpecificHandle(void* pPlatformSpecificHandle, FunctionID functionId) +{ + _ASSERTE(!"TODO RISCV64 NYI"); + LIMITED_METHOD_CONTRACT; + + _ASSERTE(pPlatformSpecificHandle != nullptr); + _ASSERTE(functionId != 0); + + PROFILE_PLATFORM_SPECIFIC_DATA* pData = reinterpret_cast(pPlatformSpecificHandle); + pData->functionId = functionId; +} + +ProfileArgIterator::ProfileArgIterator(MetaSig* pSig, void* pPlatformSpecificHandle) + : m_argIterator(pSig) +{ + _ASSERTE(!"TODO RISCV64 NYI"); + WRAPPER_NO_CONTRACT; + + _ASSERTE(pSig != nullptr); + _ASSERTE(pPlatformSpecificHandle != nullptr); + + m_handle = pPlatformSpecificHandle; + + PROFILE_PLATFORM_SPECIFIC_DATA* pData = reinterpret_cast(pPlatformSpecificHandle); +#ifdef _DEBUG + // Unwind a frame and get the SP for the profiled method to make sure it matches + // what the JIT gave us + + // Setup the context to represent the frame that called ProfileEnterNaked + CONTEXT ctx; + memset(&ctx, 0, sizeof(CONTEXT)); + + ctx.Sp = (DWORD64)pData->probeSp; + ctx.Fp = (DWORD64)pData->Fp; + ctx.Pc = (DWORD64)pData->Pc; + + // Walk up a frame to the caller frame (called the managed method which called ProfileEnterNaked) + Thread::VirtualUnwindCallFrame(&ctx); + + _ASSERTE(pData->profiledSp == (void*)ctx.Sp); +#endif + + // Get the hidden arg if there is one + MethodDesc* pMD = FunctionIdToMethodDesc(pData->functionId); + + if ((pData->hiddenArg == nullptr) && (pMD->RequiresInstArg() || pMD->AcquiresInstMethodTableFromThis())) + { + if ((pData->flags & PROFILE_ENTER) != 0) + { + if (pMD->AcquiresInstMethodTableFromThis()) + { + pData->hiddenArg = GetThis(); + } + else + { + // On ARM64 the generic instantiation parameter comes after the optional "this" pointer. + if (m_argIterator.HasThis()) + { + pData->hiddenArg = (void*)pData->argumentRegisters.a[1]; + } + else + { + pData->hiddenArg = (void*)pData->argumentRegisters.a[0]; + } + } + } + else + { + EECodeInfo codeInfo((PCODE)pData->Pc); + + // We want to pass the caller SP here. + pData->hiddenArg = EECodeManager::GetExactGenericsToken((SIZE_T)(pData->profiledSp), &codeInfo); + } + } +} + +ProfileArgIterator::~ProfileArgIterator() +{ + _ASSERTE(!"TODO RISCV64 NYI"); + LIMITED_METHOD_CONTRACT; + + m_handle = nullptr; +} + +LPVOID ProfileArgIterator::GetNextArgAddr() +{ + _ASSERTE(!"TODO RISCV64 NYI"); + WRAPPER_NO_CONTRACT; + + _ASSERTE(m_handle != nullptr); + + PROFILE_PLATFORM_SPECIFIC_DATA* pData = reinterpret_cast(m_handle); + + if ((pData->flags & (PROFILE_LEAVE | PROFILE_TAILCALL)) != 0) + { + _ASSERTE(!"GetNextArgAddr() - arguments are not available in leave and tailcall probes"); + return nullptr; + } + + int argOffset = m_argIterator.GetNextOffset(); + + if (argOffset == TransitionBlock::InvalidOffset) + { + return nullptr; + } + + if (TransitionBlock::IsFloatArgumentRegisterOffset(argOffset)) + { + return (LPBYTE)&pData->floatArgumentRegisters + (argOffset - TransitionBlock::GetOffsetOfFloatArgumentRegisters()); + } + + LPVOID pArg = nullptr; + + if (TransitionBlock::IsArgumentRegisterOffset(argOffset)) + { + pArg = (LPBYTE)&pData->argumentRegisters + (argOffset - TransitionBlock::GetOffsetOfArgumentRegisters()); + } + else + { + _ASSERTE(TransitionBlock::IsStackArgumentOffset(argOffset)); + + pArg = (LPBYTE)pData->profiledSp + (argOffset - TransitionBlock::GetOffsetOfArgs()); + } + + if (m_argIterator.IsArgPassedByRef()) + { + pArg = *(LPVOID*)pArg; + } + + return pArg; +} + +LPVOID ProfileArgIterator::GetHiddenArgValue(void) +{ + _ASSERTE(!"TODO RISCV64 NYI"); + LIMITED_METHOD_CONTRACT; + + PROFILE_PLATFORM_SPECIFIC_DATA* pData = reinterpret_cast(m_handle); + + return pData->hiddenArg; +} + +LPVOID ProfileArgIterator::GetThis(void) +{ + _ASSERTE(!"TODO RISCV64 NYI"); + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + } + CONTRACTL_END; + + PROFILE_PLATFORM_SPECIFIC_DATA* pData = (PROFILE_PLATFORM_SPECIFIC_DATA*)m_handle; + MethodDesc* pMD = FunctionIdToMethodDesc(pData->functionId); + + // We guarantee to return the correct "this" pointer in the enter probe. + // For the leave and tailcall probes, we only return a valid "this" pointer if it is the generics token. + if (pData->hiddenArg != nullptr) + { + if (pMD->AcquiresInstMethodTableFromThis()) + { + return pData->hiddenArg; + } + } + + if ((pData->flags & PROFILE_ENTER) != 0) + { + if (m_argIterator.HasThis()) + { + return (LPVOID)pData->argumentRegisters.a[0]; + } + } + + return nullptr; +} + +LPVOID ProfileArgIterator::GetReturnBufferAddr(void) +{ + _ASSERTE(!"TODO RISCV64 NYI"); + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + } + CONTRACTL_END; + + PROFILE_PLATFORM_SPECIFIC_DATA* pData = reinterpret_cast(m_handle); + + if ((pData->flags & PROFILE_TAILCALL) != 0) + { + _ASSERTE(!"GetReturnBufferAddr() - return buffer address is not available in tailcall probe"); + return nullptr; + } + + if (m_argIterator.HasRetBuffArg()) + { + if ((pData->flags & PROFILE_ENTER) != 0) + { + return (LPVOID)pData->x8; + } + else + { + // On ARM64 there is no requirement for the method to preserve the value stored in x8. + // In order to workaround this JIT will explicitly return the return buffer address in x0. + _ASSERTE((pData->flags & PROFILE_LEAVE) != 0); + return (LPVOID)pData->argumentRegisters.a[0]; + } + } + + UINT fpReturnSize = m_argIterator.GetFPReturnSize(); + if (fpReturnSize != 0) + { + TypeHandle thReturnValueType; + m_argIterator.GetSig()->GetReturnTypeNormalized(&thReturnValueType); + if (!thReturnValueType.IsNull() && thReturnValueType.IsHFA()) + { + UINT hfaFieldSize = fpReturnSize / 4; + UINT totalSize = m_argIterator.GetSig()->GetReturnTypeSize(); + _ASSERTE(totalSize % hfaFieldSize == 0); + _ASSERTE(totalSize <= 16); + + BYTE *dest = pData->buffer; + for (UINT floatRegIdx = 0; floatRegIdx < totalSize / hfaFieldSize; ++floatRegIdx) + { + if (hfaFieldSize == 4) + { + *(UINT32*)dest = *(UINT32*)&pData->floatArgumentRegisters.f[floatRegIdx]; + dest += 4; + } + else if (hfaFieldSize == 8) + { + *(UINT64*)dest = *(UINT64*)&pData->floatArgumentRegisters.f[floatRegIdx]; + dest += 8; + } + else + { + _ASSERTE(!"unimplemented on RISCV64 yet!"); +#if 0 + _ASSERTE(hfaFieldSize == 16); + *(NEON128*)dest = pData->floatArgumentRegisters.f[floatRegIdx]; + dest += 16; +#endif + } + + if (floatRegIdx > 8) + { + // There's only space for 8 arguments in buffer + _ASSERTE(FALSE); + break; + } + } + + return pData->buffer; + } + + return &pData->floatArgumentRegisters.f[0]; + } + + if (!m_argIterator.GetSig()->IsReturnTypeVoid()) + { + return &pData->argumentRegisters.a[0]; + } + + return nullptr; +} + +#undef PROFILE_ENTER +#undef PROFILE_LEAVE +#undef PROFILE_TAILCALL + +#endif // PROFILING_SUPPORTED diff --git a/src/coreclr/vm/riscv64/stubs.cpp b/src/coreclr/vm/riscv64/stubs.cpp index 9a0cdd4e406d37..9d058bcad3197f 100644 --- a/src/coreclr/vm/riscv64/stubs.cpp +++ b/src/coreclr/vm/riscv64/stubs.cpp @@ -1,4 +1,1253 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +// +// File: stubs.cpp +// +// This file contains stub functions for unimplemented features need to +// run on the ARM64 platform. -#error "TODO-RISCV64: missing implementation" +#include "common.h" +#include "dllimportcallback.h" +#include "comdelegate.h" +#include "asmconstants.h" +#include "virtualcallstub.h" +#include "jitinterface.h" +#include "ecall.h" + + +#ifndef DACCESS_COMPILE +//----------------------------------------------------------------------- +// InstructionFormat for JAL/JALR (unconditional jump) +//----------------------------------------------------------------------- +class BranchInstructionFormat : public InstructionFormat +{ + // Encoding of the VariationCode: + // bit(0) indicates whether this is a direct or an indirect jump. + // bit(1) indicates whether this is a branch with link -a.k.a call- + + public: + enum VariationCodes + { + BIF_VAR_INDIRECT = 0x00000001, + BIF_VAR_CALL = 0x00000002, + + BIF_VAR_JUMP = 0x00000000, + BIF_VAR_INDIRECT_CALL = 0x00000003 + }; + private: + BOOL IsIndirect(UINT variationCode) + { + return (variationCode & BIF_VAR_INDIRECT) != 0; + } + BOOL IsCall(UINT variationCode) + { + return (variationCode & BIF_VAR_CALL) != 0; + } + + + public: + BranchInstructionFormat() : InstructionFormat(InstructionFormat::k64) + { + LIMITED_METHOD_CONTRACT; + } + + virtual UINT GetSizeOfInstruction(UINT refSize, UINT variationCode) + { + LIMITED_METHOD_CONTRACT; + _ASSERTE(refSize == InstructionFormat::k64); + + if (IsIndirect(variationCode)) + return 16; + else + return 12; + } + + virtual UINT GetSizeOfData(UINT refSize, UINT variationCode) + { + WRAPPER_NO_CONTRACT; + return 8; + } + + + virtual UINT GetHotSpotOffset(UINT refsize, UINT variationCode) + { + WRAPPER_NO_CONTRACT; + return 0; + } + + virtual BOOL CanReach(UINT refSize, UINT variationCode, BOOL fExternal, INT_PTR offset) + { + if (fExternal) + { + // Note that the parameter 'offset' is not an offset but the target address itself (when fExternal is true) + return (refSize == InstructionFormat::k64); + } + else + { + return ((offset >= -0x80000000L && offset <= 0x7fffffff) || (refSize == InstructionFormat::k64)); + } + } + + virtual VOID EmitInstruction(UINT refSize, __int64 fixedUpReference, BYTE *pOutBufferRX, BYTE *pOutBufferRW, UINT variationCode, BYTE *pDataBuffer) + { + LIMITED_METHOD_CONTRACT; + + if (IsIndirect(variationCode)) + { + _ASSERTE(((UINT_PTR)pDataBuffer & 7) == 0); + + __int64 dataOffset = pDataBuffer - pOutBufferRW; + + if ((dataOffset < -(0x80000000L)) || (dataOffset > 0x7fffffff)) + COMPlusThrow(kNotSupportedException); + + UINT16 imm12 = (UINT16)(0xFFF & dataOffset); + //auipc t1, dataOffset[31:12] + //ld t1, t1, dataOffset[11:0] + //ld t1, t1, 0 + //jalr x0/1, t1,0 + + *(DWORD*)pOutBufferRW = 0x00000317 | (((dataOffset + 0x800) >> 12) << 12); // auipc t1, dataOffset[31:12] + *(DWORD*)(pOutBufferRW + 4) = 0x00033303 | (imm12 << 20); // ld t1, t1, dataOffset[11:0] + *(DWORD*)(pOutBufferRW + 8) = 0x00033303; // ld t1, 0(t1) + if (IsCall(variationCode)) + { + *(DWORD*)(pOutBufferRW + 12) = 0x000300e7; // jalr ra, t1, 0 + } + else + { + *(DWORD*)(pOutBufferRW + 12) = 0x00030067 ;// jalr x0, t1,0 + } + + *((__int64*)pDataBuffer) = fixedUpReference + (__int64)pOutBufferRX; + } + else + { + _ASSERTE(((UINT_PTR)pDataBuffer & 7) == 0); + + __int64 dataOffset = pDataBuffer - pOutBufferRW; + + if ((dataOffset < -(0x80000000L)) || (dataOffset > 0x7fffffff)) + COMPlusThrow(kNotSupportedException); + + UINT16 imm12 = (UINT16)(0xFFF & dataOffset); + //auipc t1, dataOffset[31:12] + //ld t1, t1, dataOffset[11:0] + //jalr x0/1, t1,0 + + *(DWORD*)pOutBufferRW = 0x00000317 | (((dataOffset + 0x800) >> 12) << 12);// auipc t1, dataOffset[31:12] + *(DWORD*)(pOutBufferRW + 4) = 0x00033303 | (imm12 << 20); // ld t1, t1, dataOffset[11:0] + if (IsCall(variationCode)) + { + *(DWORD*)(pOutBufferRW + 8) = 0x000300e7; // jalr ra, t1, 0 + } + else + { + *(DWORD*)(pOutBufferRW + 8) = 0x00030067 ;// jalr x0, t1,0 + } + + if (!ClrSafeInt<__int64>::addition(fixedUpReference, (__int64)pOutBufferRX, fixedUpReference)) + COMPlusThrowArithmetic(); + *((__int64*)pDataBuffer) = fixedUpReference; + } + } +}; + +static BYTE gBranchIF[sizeof(BranchInstructionFormat)]; + +#endif + +void ClearRegDisplayArgumentAndScratchRegisters(REGDISPLAY * pRD) +{ + pRD->volatileCurrContextPointers.R0 = NULL; + pRD->volatileCurrContextPointers.A0 = NULL; + pRD->volatileCurrContextPointers.A1 = NULL; + pRD->volatileCurrContextPointers.A2 = NULL; + pRD->volatileCurrContextPointers.A3 = NULL; + pRD->volatileCurrContextPointers.A4 = NULL; + pRD->volatileCurrContextPointers.A5 = NULL; + pRD->volatileCurrContextPointers.A6 = NULL; + pRD->volatileCurrContextPointers.A7 = NULL; + pRD->volatileCurrContextPointers.T0 = NULL; + pRD->volatileCurrContextPointers.T1 = NULL; + pRD->volatileCurrContextPointers.T2 = NULL; + pRD->volatileCurrContextPointers.T3 = NULL; + pRD->volatileCurrContextPointers.T4 = NULL; + pRD->volatileCurrContextPointers.T5 = NULL; + pRD->volatileCurrContextPointers.T6 = NULL; +} + +void LazyMachState::unwindLazyState(LazyMachState* baseState, + MachState* unwoundstate, + DWORD threadId, + int funCallDepth, + HostCallPreference hostCallPreference) +{ + T_CONTEXT context; + T_KNONVOLATILE_CONTEXT_POINTERS nonVolContextPtrs; + + context.ContextFlags = 0; // Read by PAL_VirtualUnwind. + + context.Fp = unwoundstate->captureCalleeSavedRegisters[0] = baseState->captureCalleeSavedRegisters[0]; + context.S1 = unwoundstate->captureCalleeSavedRegisters[1] = baseState->captureCalleeSavedRegisters[1]; + context.S2 = unwoundstate->captureCalleeSavedRegisters[2] = baseState->captureCalleeSavedRegisters[2]; + context.S3 = unwoundstate->captureCalleeSavedRegisters[3] = baseState->captureCalleeSavedRegisters[3]; + context.S4 = unwoundstate->captureCalleeSavedRegisters[4] = baseState->captureCalleeSavedRegisters[4]; + context.S5 = unwoundstate->captureCalleeSavedRegisters[5] = baseState->captureCalleeSavedRegisters[5]; + context.S6 = unwoundstate->captureCalleeSavedRegisters[6] = baseState->captureCalleeSavedRegisters[6]; + context.S7 = unwoundstate->captureCalleeSavedRegisters[7] = baseState->captureCalleeSavedRegisters[7]; + context.S8 = unwoundstate->captureCalleeSavedRegisters[8] = baseState->captureCalleeSavedRegisters[8]; + context.S9 = unwoundstate->captureCalleeSavedRegisters[9] = baseState->captureCalleeSavedRegisters[9]; + context.S10 = unwoundstate->captureCalleeSavedRegisters[10] = baseState->captureCalleeSavedRegisters[10]; + context.S11 = unwoundstate->captureCalleeSavedRegisters[11] = baseState->captureCalleeSavedRegisters[11]; + context.Gp = unwoundstate->captureCalleeSavedRegisters[12] = baseState->captureCalleeSavedRegisters[12]; + context.Tp = unwoundstate->captureCalleeSavedRegisters[13] = baseState->captureCalleeSavedRegisters[13]; + context.Ra = NULL; // Filled by the unwinder + + context.Sp = baseState->captureSp; + context.Pc = baseState->captureIp; + +#if !defined(DACCESS_COMPILE) + // For DAC, if we get here, it means that the LazyMachState is uninitialized and we have to unwind it. + // The API we use to unwind in DAC is StackWalk64(), which does not support the context pointers. + // + // Restore the integer registers to KNONVOLATILE_CONTEXT_POINTERS to be used for unwinding. + nonVolContextPtrs.Fp = &unwoundstate->captureCalleeSavedRegisters[0]; + nonVolContextPtrs.S1 = &unwoundstate->captureCalleeSavedRegisters[1]; + nonVolContextPtrs.S2 = &unwoundstate->captureCalleeSavedRegisters[2]; + nonVolContextPtrs.S3 = &unwoundstate->captureCalleeSavedRegisters[3]; + nonVolContextPtrs.S4 = &unwoundstate->captureCalleeSavedRegisters[4]; + nonVolContextPtrs.S5 = &unwoundstate->captureCalleeSavedRegisters[5]; + nonVolContextPtrs.S6 = &unwoundstate->captureCalleeSavedRegisters[6]; + nonVolContextPtrs.S7 = &unwoundstate->captureCalleeSavedRegisters[7]; + nonVolContextPtrs.S8 = &unwoundstate->captureCalleeSavedRegisters[8]; + nonVolContextPtrs.S9 = &unwoundstate->captureCalleeSavedRegisters[9]; + nonVolContextPtrs.S10 = &unwoundstate->captureCalleeSavedRegisters[10]; + nonVolContextPtrs.S11 = &unwoundstate->captureCalleeSavedRegisters[11]; + nonVolContextPtrs.Gp = &unwoundstate->captureCalleeSavedRegisters[12]; + nonVolContextPtrs.Tp = &unwoundstate->captureCalleeSavedRegisters[13]; + nonVolContextPtrs.Ra = NULL; // Filled by the unwinder + +#endif // DACCESS_COMPILE + + LOG((LF_GCROOTS, LL_INFO100000, "STACKWALK LazyMachState::unwindLazyState(ip:%p,sp:%p)\n", baseState->captureIp, baseState->captureSp)); + + PCODE pvControlPc; + + do { + +#ifndef TARGET_UNIX + pvControlPc = Thread::VirtualUnwindCallFrame(&context, &nonVolContextPtrs); +#else // !TARGET_UNIX +#ifdef DACCESS_COMPILE + HRESULT hr = DacVirtualUnwind(threadId, &context, &nonVolContextPtrs); + if (FAILED(hr)) + { + DacError(hr); + } +#else // DACCESS_COMPILE + BOOL success = PAL_VirtualUnwind(&context, &nonVolContextPtrs); + if (!success) + { + _ASSERTE(!"unwindLazyState: Unwinding failed"); + EEPOLICY_HANDLE_FATAL_ERROR(COR_E_EXECUTIONENGINE); + } +#endif // DACCESS_COMPILE + pvControlPc = GetIP(&context); +#endif // !TARGET_UNIX + + if (funCallDepth > 0) + { + funCallDepth--; + if (funCallDepth == 0) + break; + } + else + { + // Determine whether given IP resides in JITted code. (It returns nonzero in that case.) + // Use it now to see if we've unwound to managed code yet. + BOOL fFailedReaderLock = FALSE; + BOOL fIsManagedCode = ExecutionManager::IsManagedCode(pvControlPc, hostCallPreference, &fFailedReaderLock); + if (fFailedReaderLock) + { + // We don't know if we would have been able to find a JIT + // manager, because we couldn't enter the reader lock without + // yielding (and our caller doesn't want us to yield). So abort + // now. + + // Invalidate the lazyState we're returning, so the caller knows + // we aborted before we could fully unwind + unwoundstate->_isValid = false; + return; + } + + if (fIsManagedCode) + break; + + } + } while (true); + +#ifdef TARGET_UNIX + unwoundstate->captureCalleeSavedRegisters[0] = context.Fp; + unwoundstate->captureCalleeSavedRegisters[1] = context.S1; + unwoundstate->captureCalleeSavedRegisters[2] = context.S2; + unwoundstate->captureCalleeSavedRegisters[3] = context.S3; + unwoundstate->captureCalleeSavedRegisters[4] = context.S4; + unwoundstate->captureCalleeSavedRegisters[5] = context.S5; + unwoundstate->captureCalleeSavedRegisters[6] = context.S6; + unwoundstate->captureCalleeSavedRegisters[7] = context.S7; + unwoundstate->captureCalleeSavedRegisters[8] = context.S8; + unwoundstate->captureCalleeSavedRegisters[9] = context.S9; + unwoundstate->captureCalleeSavedRegisters[10] = context.S10; + unwoundstate->captureCalleeSavedRegisters[11] = context.S11; + unwoundstate->captureCalleeSavedRegisters[12] = context.Gp; + unwoundstate->captureCalleeSavedRegisters[13] = context.Tp; +#endif + +#ifdef DACCESS_COMPILE + // For DAC builds, we update the registers directly since we dont have context pointers + unwoundstate->captureCalleeSavedRegisters[0] = context.Fp; + unwoundstate->captureCalleeSavedRegisters[1] = context.S1; + unwoundstate->captureCalleeSavedRegisters[2] = context.S2; + unwoundstate->captureCalleeSavedRegisters[3] = context.S3; + unwoundstate->captureCalleeSavedRegisters[4] = context.S4; + unwoundstate->captureCalleeSavedRegisters[5] = context.S5; + unwoundstate->captureCalleeSavedRegisters[6] = context.S6; + unwoundstate->captureCalleeSavedRegisters[7] = context.S7; + unwoundstate->captureCalleeSavedRegisters[8] = context.S8; + unwoundstate->captureCalleeSavedRegisters[9] = context.S9; + unwoundstate->captureCalleeSavedRegisters[10] = context.S10; + unwoundstate->captureCalleeSavedRegisters[11] = context.S11; + unwoundstate->captureCalleeSavedRegisters[12] = context.Gp; + unwoundstate->captureCalleeSavedRegisters[13] = context.Tp; +#else // !DACCESS_COMPILE + // For non-DAC builds, update the register state from context pointers + unwoundstate->ptrCalleeSavedRegisters[0] = nonVolContextPtrs.Fp; + unwoundstate->ptrCalleeSavedRegisters[1] = nonVolContextPtrs.S1; + unwoundstate->ptrCalleeSavedRegisters[2] = nonVolContextPtrs.S2; + unwoundstate->ptrCalleeSavedRegisters[3] = nonVolContextPtrs.S3; + unwoundstate->ptrCalleeSavedRegisters[4] = nonVolContextPtrs.S4; + unwoundstate->ptrCalleeSavedRegisters[5] = nonVolContextPtrs.S5; + unwoundstate->ptrCalleeSavedRegisters[6] = nonVolContextPtrs.S6; + unwoundstate->ptrCalleeSavedRegisters[7] = nonVolContextPtrs.S7; + unwoundstate->ptrCalleeSavedRegisters[8] = nonVolContextPtrs.S8; + unwoundstate->ptrCalleeSavedRegisters[9] = nonVolContextPtrs.S9; + unwoundstate->ptrCalleeSavedRegisters[10] = nonVolContextPtrs.S10; + unwoundstate->ptrCalleeSavedRegisters[11] = nonVolContextPtrs.S11; + unwoundstate->ptrCalleeSavedRegisters[12] = nonVolContextPtrs.Gp; + unwoundstate->ptrCalleeSavedRegisters[13] = nonVolContextPtrs.Tp; +#endif // DACCESS_COMPILE + + unwoundstate->_pc = context.Pc; + unwoundstate->_sp = context.Sp; + + unwoundstate->_isValid = TRUE; +} + +void HelperMethodFrame::UpdateRegDisplay(const PREGDISPLAY pRD) +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + MODE_ANY; + SUPPORTS_DAC; + } + CONTRACTL_END; + + pRD->IsCallerContextValid = FALSE; + pRD->IsCallerSPValid = FALSE; // Don't add usage of this field. This is only temporary. + + // + // Copy the saved state from the frame to the current context. + // + + LOG((LF_GCROOTS, LL_INFO100000, "STACKWALK HelperMethodFrame::UpdateRegDisplay cached ip:%p, sp:%p\n", m_MachState._pc, m_MachState._sp)); + + #if defined(DACCESS_COMPILE) + // For DAC, we may get here when the HMF is still uninitialized. + // So we may need to unwind here. + if (!m_MachState.isValid()) + { + // This allocation throws on OOM. + MachState* pUnwoundState = (MachState*)DacAllocHostOnlyInstance(sizeof(*pUnwoundState), true); + + InsureInit(false, pUnwoundState); + + pRD->pCurrentContext->Pc = pRD->ControlPC = pUnwoundState->_pc; + pRD->pCurrentContext->Sp = pRD->SP = pUnwoundState->_sp; + pRD->pCurrentContext->Fp = (DWORD64)(pUnwoundState->captureCalleeSavedRegisters[0]); + pRD->pCurrentContext->S1 = (DWORD64)(pUnwoundState->captureCalleeSavedRegisters[1]); + pRD->pCurrentContext->S2 = (DWORD64)(pUnwoundState->captureCalleeSavedRegisters[2]); + pRD->pCurrentContext->S3 = (DWORD64)(pUnwoundState->captureCalleeSavedRegisters[3]); + pRD->pCurrentContext->S4 = (DWORD64)(pUnwoundState->captureCalleeSavedRegisters[4]); + pRD->pCurrentContext->S5 = (DWORD64)(pUnwoundState->captureCalleeSavedRegisters[5]); + pRD->pCurrentContext->S6 = (DWORD64)(pUnwoundState->captureCalleeSavedRegisters[6]); + pRD->pCurrentContext->S7 = (DWORD64)(pUnwoundState->captureCalleeSavedRegisters[7]); + pRD->pCurrentContext->S8 = (DWORD64)(pUnwoundState->captureCalleeSavedRegisters[8]); + pRD->pCurrentContext->S9 = (DWORD64)(pUnwoundState->captureCalleeSavedRegisters[9]); + pRD->pCurrentContext->S10 = (DWORD64)(pUnwoundState->captureCalleeSavedRegisters[10]); + pRD->pCurrentContext->S11 = (DWORD64)(pUnwoundState->captureCalleeSavedRegisters[11]); + pRD->pCurrentContext->Gp = (DWORD64)(pUnwoundState->captureCalleeSavedRegisters[12]); + pRD->pCurrentContext->Tp = (DWORD64)(pUnwoundState->captureCalleeSavedRegisters[13]); + pRD->pCurrentContext->Ra = NULL; // Unwind again to get Caller's PC + + pRD->pCurrentContextPointers->Fp = pUnwoundState->ptrCalleeSavedRegisters[0]; + pRD->pCurrentContextPointers->S1 = pUnwoundState->ptrCalleeSavedRegisters[1]; + pRD->pCurrentContextPointers->S2 = pUnwoundState->ptrCalleeSavedRegisters[2]; + pRD->pCurrentContextPointers->S3 = pUnwoundState->ptrCalleeSavedRegisters[3]; + pRD->pCurrentContextPointers->S4 = pUnwoundState->ptrCalleeSavedRegisters[4]; + pRD->pCurrentContextPointers->S5 = pUnwoundState->ptrCalleeSavedRegisters[5]; + pRD->pCurrentContextPointers->S6 = pUnwoundState->ptrCalleeSavedRegisters[6]; + pRD->pCurrentContextPointers->S7 = pUnwoundState->ptrCalleeSavedRegisters[7]; + pRD->pCurrentContextPointers->S8 = pUnwoundState->ptrCalleeSavedRegisters[8]; + pRD->pCurrentContextPointers->S9 = pUnwoundState->ptrCalleeSavedRegisters[9]; + pRD->pCurrentContextPointers->S10 = pUnwoundState->ptrCalleeSavedRegisters[10]; + pRD->pCurrentContextPointers->S11 = pUnwoundState->ptrCalleeSavedRegisters[11]; + pRD->pCurrentContextPointers->Gp = pUnwoundState->ptrCalleeSavedRegisters[12]; + pRD->pCurrentContextPointers->Tp = pUnwoundState->ptrCalleeSavedRegisters[13]; + pRD->pCurrentContextPointers->Ra = NULL; + return; + } +#endif // DACCESS_COMPILE + + // reset pContext; it's only valid for active (top-most) frame + pRD->pContext = NULL; + pRD->ControlPC = GetReturnAddress(); // m_MachState._pc; + pRD->SP = (DWORD64)(size_t)m_MachState._sp; + + pRD->pCurrentContext->Pc = pRD->ControlPC; + pRD->pCurrentContext->Sp = pRD->SP; + +#ifdef TARGET_UNIX + pRD->pCurrentContext->Fp = m_MachState.ptrCalleeSavedRegisters[0] ? *m_MachState.ptrCalleeSavedRegisters[0] : m_MachState.captureCalleeSavedRegisters[0]; + pRD->pCurrentContext->S1 = m_MachState.ptrCalleeSavedRegisters[1] ? *m_MachState.ptrCalleeSavedRegisters[1] : m_MachState.captureCalleeSavedRegisters[1]; + pRD->pCurrentContext->S2 = m_MachState.ptrCalleeSavedRegisters[2] ? *m_MachState.ptrCalleeSavedRegisters[2] : m_MachState.captureCalleeSavedRegisters[2]; + pRD->pCurrentContext->S3 = m_MachState.ptrCalleeSavedRegisters[3] ? *m_MachState.ptrCalleeSavedRegisters[3] : m_MachState.captureCalleeSavedRegisters[3]; + pRD->pCurrentContext->S4 = m_MachState.ptrCalleeSavedRegisters[4] ? *m_MachState.ptrCalleeSavedRegisters[4] : m_MachState.captureCalleeSavedRegisters[4]; + pRD->pCurrentContext->S5 = m_MachState.ptrCalleeSavedRegisters[5] ? *m_MachState.ptrCalleeSavedRegisters[5] : m_MachState.captureCalleeSavedRegisters[5]; + pRD->pCurrentContext->S6 = m_MachState.ptrCalleeSavedRegisters[6] ? *m_MachState.ptrCalleeSavedRegisters[6] : m_MachState.captureCalleeSavedRegisters[6]; + pRD->pCurrentContext->S7 = m_MachState.ptrCalleeSavedRegisters[7] ? *m_MachState.ptrCalleeSavedRegisters[7] : m_MachState.captureCalleeSavedRegisters[7]; + pRD->pCurrentContext->S8 = m_MachState.ptrCalleeSavedRegisters[8] ? *m_MachState.ptrCalleeSavedRegisters[8] : m_MachState.captureCalleeSavedRegisters[8]; + pRD->pCurrentContext->S9 = m_MachState.ptrCalleeSavedRegisters[9] ? *m_MachState.ptrCalleeSavedRegisters[9] : m_MachState.captureCalleeSavedRegisters[9]; + pRD->pCurrentContext->S10 = m_MachState.ptrCalleeSavedRegisters[10] ? *m_MachState.ptrCalleeSavedRegisters[10] : m_MachState.captureCalleeSavedRegisters[10]; + pRD->pCurrentContext->S11 = m_MachState.ptrCalleeSavedRegisters[11] ? *m_MachState.ptrCalleeSavedRegisters[11] : m_MachState.captureCalleeSavedRegisters[11]; + pRD->pCurrentContext->Gp = m_MachState.ptrCalleeSavedRegisters[12] ? *m_MachState.ptrCalleeSavedRegisters[12] : m_MachState.captureCalleeSavedRegisters[12]; + pRD->pCurrentContext->Tp = m_MachState.ptrCalleeSavedRegisters[13] ? *m_MachState.ptrCalleeSavedRegisters[13] : m_MachState.captureCalleeSavedRegisters[13]; + pRD->pCurrentContext->Ra = NULL; // Unwind again to get Caller's PC +#else // TARGET_UNIX + pRD->pCurrentContext->Fp = *m_MachState.ptrCalleeSavedRegisters[0]; + pRD->pCurrentContext->S1 = *m_MachState.ptrCalleeSavedRegisters[1]; + pRD->pCurrentContext->S2 = *m_MachState.ptrCalleeSavedRegisters[2]; + pRD->pCurrentContext->S3 = *m_MachState.ptrCalleeSavedRegisters[3]; + pRD->pCurrentContext->S4 = *m_MachState.ptrCalleeSavedRegisters[4]; + pRD->pCurrentContext->S5 = *m_MachState.ptrCalleeSavedRegisters[5]; + pRD->pCurrentContext->S6 = *m_MachState.ptrCalleeSavedRegisters[6]; + pRD->pCurrentContext->S7 = *m_MachState.ptrCalleeSavedRegisters[7]; + pRD->pCurrentContext->S8 = *m_MachState.ptrCalleeSavedRegisters[8]; + pRD->pCurrentContext->S9 = *m_MachState.ptrCalleeSavedRegisters[9]; + pRD->pCurrentContext->S10 = *m_MachState.ptrCalleeSavedRegisters[10]; + pRD->pCurrentContext->S11 = *m_MachState.ptrCalleeSavedRegisters[11]; + pRD->pCurrentContext->Gp = *m_MachState.ptrCalleeSavedRegisters[12]; + pRD->pCurrentContext->Tp = *m_MachState.ptrCalleeSavedRegisters[13]; + pRD->pCurrentContext->Ra = NULL; // Unwind again to get Caller's PC +#endif + +#if !defined(DACCESS_COMPILE) + pRD->pCurrentContextPointers->Fp = m_MachState.ptrCalleeSavedRegisters[0]; + pRD->pCurrentContextPointers->S1 = m_MachState.ptrCalleeSavedRegisters[1]; + pRD->pCurrentContextPointers->S2 = m_MachState.ptrCalleeSavedRegisters[2]; + pRD->pCurrentContextPointers->S3 = m_MachState.ptrCalleeSavedRegisters[3]; + pRD->pCurrentContextPointers->S4 = m_MachState.ptrCalleeSavedRegisters[4]; + pRD->pCurrentContextPointers->S5 = m_MachState.ptrCalleeSavedRegisters[5]; + pRD->pCurrentContextPointers->S6 = m_MachState.ptrCalleeSavedRegisters[6]; + pRD->pCurrentContextPointers->S7 = m_MachState.ptrCalleeSavedRegisters[7]; + pRD->pCurrentContextPointers->S8 = m_MachState.ptrCalleeSavedRegisters[8]; + pRD->pCurrentContextPointers->S9 = m_MachState.ptrCalleeSavedRegisters[9]; + pRD->pCurrentContextPointers->S10 = m_MachState.ptrCalleeSavedRegisters[10]; + pRD->pCurrentContextPointers->S11 = m_MachState.ptrCalleeSavedRegisters[11]; + pRD->pCurrentContextPointers->Gp = m_MachState.ptrCalleeSavedRegisters[12]; + pRD->pCurrentContextPointers->Tp = m_MachState.ptrCalleeSavedRegisters[13]; + pRD->pCurrentContextPointers->Ra = NULL; // Unwind again to get Caller's PC +#endif + ClearRegDisplayArgumentAndScratchRegisters(pRD); +} + +#ifndef DACCESS_COMPILE +void ThisPtrRetBufPrecode::Init(MethodDesc* pMD, LoaderAllocator *pLoaderAllocator) +{ + _ASSERTE(!"RISCV64: not implementation on riscv64!!!"); +} + +#endif // !DACCESS_COMPILE + +void UpdateRegDisplayFromCalleeSavedRegisters(REGDISPLAY * pRD, CalleeSavedRegisters * pCalleeSaved) +{ + _ASSERTE(!"RISCV64: not implementation on riscv64!!!"); +} + + +void TransitionFrame::UpdateRegDisplay(const PREGDISPLAY pRD) +{ + _ASSERTE(!"RISCV64: not implementation on riscv64!!!"); +} + + + +void FaultingExceptionFrame::UpdateRegDisplay(const PREGDISPLAY pRD) +{ + _ASSERTE(!"RISCV64: not implementation on riscv64!!!"); +} + +void InlinedCallFrame::UpdateRegDisplay(const PREGDISPLAY pRD) +{ + _ASSERTE(!"RISCV64: not implementation on riscv64!!!"); + RETURN; +} + +#ifdef FEATURE_HIJACK +TADDR ResumableFrame::GetReturnAddressPtr(void) +{ + _ASSERTE(!"RISCV64: not implementation on riscv64!!!"); + LIMITED_METHOD_DAC_CONTRACT; + return dac_cast(m_Regs) + offsetof(T_CONTEXT, Pc); +} + +void ResumableFrame::UpdateRegDisplay(const PREGDISPLAY pRD) +{ + _ASSERTE(!"RISCV64: not implementation on riscv64!!!"); + RETURN; +} + +void HijackFrame::UpdateRegDisplay(const PREGDISPLAY pRD) +{ + _ASSERTE(!"RISCV64: not implementation on riscv64!!!"); +} +#endif // FEATURE_HIJACK + +#ifdef FEATURE_COMINTEROP + +void emitCOMStubCall (ComCallMethodDesc *pCOMMethodRX, ComCallMethodDesc *pCOMMethodRW, PCODE target) +{ + _ASSERTE(!"RISCV64: not implementation on riscv64!!!"); +} +#endif // FEATURE_COMINTEROP + +void JIT_TailCall() +{ + _ASSERTE(!"RISCV64:NYI"); +} + +#if !defined(DACCESS_COMPILE) +EXTERN_C void JIT_UpdateWriteBarrierState(bool skipEphemeralCheck, size_t writeableOffset); + +extern "C" void STDCALL JIT_PatchedCodeStart(); +extern "C" void STDCALL JIT_PatchedCodeLast(); + +static void UpdateWriteBarrierState(bool skipEphemeralCheck) +{ + BYTE *writeBarrierCodeStart = GetWriteBarrierCodeLocation((void*)JIT_PatchedCodeStart); + BYTE *writeBarrierCodeStartRW = writeBarrierCodeStart; + ExecutableWriterHolderNoLog writeBarrierWriterHolder; + if (IsWriteBarrierCopyEnabled()) + { + writeBarrierWriterHolder.AssignExecutableWriterHolder(writeBarrierCodeStart, (BYTE*)JIT_PatchedCodeLast - (BYTE*)JIT_PatchedCodeStart); + writeBarrierCodeStartRW = writeBarrierWriterHolder.GetRW(); + } + JIT_UpdateWriteBarrierState(GCHeapUtilities::IsServerHeap(), writeBarrierCodeStartRW - writeBarrierCodeStart); +} + +void InitJITHelpers1() +{ + STANDARD_VM_CONTRACT; + + _ASSERTE(g_SystemInfo.dwNumberOfProcessors != 0); + + // Allocation helpers, faster but non-logging + if (!((TrackAllocationsEnabled()) || + (LoggingOn(LF_GCALLOC, LL_INFO10)) +#ifdef _DEBUG + || (g_pConfig->ShouldInjectFault(INJECTFAULT_GCHEAP) != 0) +#endif // _DEBUG + )) + { + if (GCHeapUtilities::UseThreadAllocationContexts()) + { + SetJitHelperFunction(CORINFO_HELP_NEWSFAST, JIT_NewS_MP_FastPortable); + SetJitHelperFunction(CORINFO_HELP_NEWSFAST_ALIGN8, JIT_NewS_MP_FastPortable); + SetJitHelperFunction(CORINFO_HELP_NEWARR_1_VC, JIT_NewArr1VC_MP_FastPortable); + SetJitHelperFunction(CORINFO_HELP_NEWARR_1_OBJ, JIT_NewArr1OBJ_MP_FastPortable); + + ECall::DynamicallyAssignFCallImpl(GetEEFuncEntryPoint(AllocateString_MP_FastPortable), ECall::FastAllocateString); + } + } + + UpdateWriteBarrierState(GCHeapUtilities::IsServerHeap()); +} + +#else +void UpdateWriteBarrierState(bool) {} +#endif // !defined(DACCESS_COMPILE) + +PTR_CONTEXT GetCONTEXTFromRedirectedStubStackFrame(T_DISPATCHER_CONTEXT * pDispatcherContext) +{ + LIMITED_METHOD_DAC_CONTRACT; + + DWORD64 stackSlot = pDispatcherContext->EstablisherFrame + REDIRECTSTUB_SP_OFFSET_CONTEXT; + PTR_PTR_CONTEXT ppContext = dac_cast((TADDR)stackSlot); + return *ppContext; +} + +PTR_CONTEXT GetCONTEXTFromRedirectedStubStackFrame(T_CONTEXT * pContext) +{ + LIMITED_METHOD_DAC_CONTRACT; + + DWORD64 stackSlot = pContext->Sp + REDIRECTSTUB_SP_OFFSET_CONTEXT; + PTR_PTR_CONTEXT ppContext = dac_cast((TADDR)stackSlot); + return *ppContext; +} + +void RedirectForThreadAbort() +{ + // ThreadAbort is not supported in .net core + throw "NYI"; +} + +#if !defined(DACCESS_COMPILE) +FaultingExceptionFrame *GetFrameFromRedirectedStubStackFrame (DISPATCHER_CONTEXT *pDispatcherContext) +{ + _ASSERTE(!"RISCV64: not implementation on riscv64!!!"); + LIMITED_METHOD_CONTRACT; + + return (FaultingExceptionFrame*)NULL; +} + + +BOOL +AdjustContextForVirtualStub( + EXCEPTION_RECORD *pExceptionRecord, + CONTEXT *pContext) +{ + LIMITED_METHOD_CONTRACT; + + Thread * pThread = GetThreadNULLOk(); + + // We may not have a managed thread object. Example is an AV on the helper thread. + // (perhaps during StubManager::IsStub) + if (pThread == NULL) + { + return FALSE; + } + + PCODE f_IP = GetIP(pContext); + + VirtualCallStubManager::StubKind sk; + VirtualCallStubManager::FindStubManager(f_IP, &sk); + + if (sk == VirtualCallStubManager::SK_DISPATCH) + { + if (*PTR_DWORD(f_IP - 4) != DISPATCH_STUB_FIRST_DWORD) + { + _ASSERTE(!"AV in DispatchStub at unknown instruction"); + return FALSE; + } + } + else + if (sk == VirtualCallStubManager::SK_RESOLVE) + { + if (*PTR_DWORD(f_IP) != RESOLVE_STUB_FIRST_DWORD) + { + _ASSERTE(!"AV in ResolveStub at unknown instruction"); + return FALSE; + } + } + else + { + return FALSE; + } + + PCODE callsite = GetAdjustedCallAddress(GetRA(pContext)); + + // Lr must already have been saved before calling so it should not be necessary to restore Lr + + if (pExceptionRecord != NULL) + { + pExceptionRecord->ExceptionAddress = (PVOID)callsite; + } + SetIP(pContext, callsite); + + return TRUE; +} +#endif // !DACCESS_COMPILE + +UMEntryThunk * UMEntryThunk::Decode(void *pCallback) +{ + _ASSERTE(offsetof(UMEntryThunkCode, m_code) == 0); + UMEntryThunkCode * pCode = (UMEntryThunkCode*)pCallback; + + // We may be called with an unmanaged external code pointer instead. So if it doesn't look like one of our + // stubs (see UMEntryThunkCode::Encode below) then we'll return NULL. Luckily in these scenarios our + // caller will perform a hash lookup on successful return to verify our result in case random unmanaged + // code happens to look like ours. + if ((pCode->m_code[0] == 0x00009f97) && // auipc t6, 0 + (pCode->m_code[1] == 0x018fb383) && // ld t2, 24(t6) + (pCode->m_code[2] == 0x010fbf83) && // ld t6, 16(t6) + (pCode->m_code[3] == 0x000f8067)) // jalr x0, 0(t6) + { + return (UMEntryThunk*)pCode->m_pvSecretParam; + } + + return NULL; +} + +void UMEntryThunkCode::Encode(UMEntryThunkCode *pEntryThunkCodeRX, BYTE* pTargetCode, void* pvSecretParam) +{ + // auipc t6, 0 + // ld t2, 24(t6) + // ld t6, 16(t6) + // jalr x0, 0(t6) + // m_pTargetCode data + // m_pvSecretParam data + + m_code[0] = 0x00009f97; // auipc t6, 0 + m_code[1] = 0x018fb383; // ld t2, 24(t6) + m_code[2] = 0x010fbf83; // ld t6, 16(t6) + m_code[3] = 0x000f8067; // jalr x0, 0(t6) + + m_pTargetCode = (TADDR)pTargetCode; + m_pvSecretParam = (TADDR)pvSecretParam; + FlushInstructionCache(GetCurrentProcess(),&pEntryThunkCodeRX->m_code,sizeof(m_code)); +} + +#ifndef DACCESS_COMPILE + +void UMEntryThunkCode::Poison() +{ + _ASSERTE(!"RISCV64: not implementation on riscv64!!!"); +} + +#endif // DACCESS_COMPILE + +#if !defined(DACCESS_COMPILE) +VOID ResetCurrentContext() +{ + LIMITED_METHOD_CONTRACT; +} +#endif + +LONG CLRNoCatchHandler(EXCEPTION_POINTERS* pExceptionInfo, PVOID pv) +{ + return EXCEPTION_CONTINUE_SEARCH; +} + +void FlushWriteBarrierInstructionCache() +{ + // this wouldn't be called in arm64, just to comply with gchelpers.h +} + +int StompWriteBarrierEphemeral(bool isRuntimeSuspended) +{ + UpdateWriteBarrierState(GCHeapUtilities::IsServerHeap()); + return SWB_PASS; +} + +int StompWriteBarrierResize(bool isRuntimeSuspended, bool bReqUpperBoundsCheck) +{ + UpdateWriteBarrierState(GCHeapUtilities::IsServerHeap()); + return SWB_PASS; +} + +#ifdef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP +int SwitchToWriteWatchBarrier(bool isRuntimeSuspended) +{ + UpdateWriteBarrierState(GCHeapUtilities::IsServerHeap()); + return SWB_PASS; +} + +int SwitchToNonWriteWatchBarrier(bool isRuntimeSuspended) +{ + UpdateWriteBarrierState(GCHeapUtilities::IsServerHeap()); + return SWB_PASS; +} +#endif // FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP + +#ifdef DACCESS_COMPILE +BOOL GetAnyThunkTarget (T_CONTEXT *pctx, TADDR *pTarget, TADDR *pTargetMethodDesc) +{ + _ASSERTE(!"RISCV64:NYI"); + return FALSE; +} +#endif // DACCESS_COMPILE + +#ifndef DACCESS_COMPILE +// ---------------------------------------------------------------- +// StubLinkerCPU methods +// ---------------------------------------------------------------- + +void StubLinkerCPU::EmitMovConstant(IntReg target, UINT64 constant) +{ + if (0 == ((constant + 0x800) >> 32)) { + if (((constant + 0x800) >> 12) != 0) + { + Emit32((DWORD)(0x00000037 | (((constant + 0x800) >> 12) << 12) | (target << 7))); // lui target, (constant + 0x800) >> 12 + if ((constant & 0xFFF) != 0) + { + Emit32((DWORD)(0x00000013 | (constant & 0xFFF) << 20 | (target << 7) | (target << 15))); // addi target, target, constant + } + } + else + { + Emit32((DWORD)(0x00000013 | (constant & 0xFFF) << 20 | (target << 7))); // addi target, x0, constant + } + } + else + { + UINT32 upper = constant >> 32; + if (((upper + 0x800) >> 12) != 0) + { + Emit32((DWORD)(0x00000037 | (((upper + 0x800) >> 12) << 12) | (target << 7))); // lui target, (upper + 0x800) >> 12 + if ((upper & 0xFFF) != 0) + { + Emit32((DWORD)(0x00000013 | (upper & 0xFFF) << 20 | (target << 7) | (target << 15))); // addi target, target, upper + } + } + else + { + Emit32((DWORD)(0x00000013 | (upper & 0xFFF) << 20 | (target << 7))); // addi target, x0, upper + } + UINT32 lower = (constant << 32) >> 32; + UINT32 shift = 0; + for (int i = 32; i >= 0; i -= 11) + { + shift += i > 11 ? 11 : i; + UINT32 current = lower >> (i < 11 ? 0 : i - 11); + if (current != 0) + { + Emit32((DWORD)(0x00001013 | (shift << 20) | (target << 7) | (target << 15))); // slli target, target, shift + Emit32((DWORD)(0x00000013 | (current & 0x7FF) << 20 | (target << 7) | (target << 15))); // addi target, target, current + shift = 0; + } + } + if (shift) + { + Emit32((DWORD)(0x00001013 | (shift << 20) | (target << 7) | (target << 15))); // slli target, target, shift + } + } +} + +void StubLinkerCPU::EmitCmpImm(IntReg reg, int imm) +{ + _ASSERTE(!"RISCV64: not implementation on riscv64!!!"); +} + +void StubLinkerCPU::EmitCmpReg(IntReg Xn, IntReg Xm) +{ + _ASSERTE(!"RISCV64: not implementation on riscv64!!!"); +} + +void StubLinkerCPU::EmitCondFlagJump(CodeLabel * target, UINT cond) +{ + _ASSERTE(!"RISCV64: not implementation on riscv64!!!"); +} + +void StubLinkerCPU::EmitJumpRegister(IntReg regTarget) +{ + Emit32(0x00000067 | (regTarget << 15)); +} + +void StubLinkerCPU::EmitRet(IntReg Xn) +{ + Emit32((DWORD)(0x00000067 | (Xn << 15))); // jalr X0, 0(Xn) +} + +void StubLinkerCPU::EmitLoadStoreRegPairImm(DWORD flags, IntReg Xt1, IntReg Xt2, IntReg Xn, int offset) +{ + _ASSERTE((-1024 <= offset) && (offset <= 1015)); + _ASSERTE((offset & 7) == 0); + + BOOL isLoad = flags & 1; + if (isLoad) { + // ld Xt1, offset(Xn)); + Emit32((DWORD)(0x00003003 | (Xt1 << 7) | (Xn << 15) | (offset << 20))); + // ld Xt2, (offset+8)(Xn)); + Emit32((DWORD)(0x00003003 | (Xt2 << 7) | (Xn << 15) | ((offset + 8) << 20))); + } else { + // sd Xt1, offset(Xn) + Emit32((DWORD)(0x00003023 | (Xt1 << 20) | (Xn << 15) | (offset & 0xF) << 7 | (((offset >> 4) & 0xFF) << 25))); + // sd Xt1, (offset + 8)(Xn) + Emit32((DWORD)(0x00003023 | (Xt2 << 20) | (Xn << 15) | ((offset + 8) & 0xF) << 7 | ((((offset + 8) >> 4) & 0xFF) << 25))); + } +} + +void StubLinkerCPU::EmitLoadStoreRegPairImm(DWORD flags, FloatReg Ft1, FloatReg Ft2, IntReg Xn, int offset) +{ + _ASSERTE((-1024 <= offset) && (offset <= 1015)); + _ASSERTE((offset & 7) == 0); + + BOOL isLoad = flags & 1; + if (isLoad) { + // fld Ft, Xn, offset + Emit32((DWORD)(0x00003007 | (Xn << 15) | (Ft1 << 7) | (offset << 20))); + // fld Ft, Xn, offset + 8 + Emit32((DWORD)(0x00003007 | (Xn << 15) | (Ft2 << 7) | ((offset + 8) << 20))); + } else { + // fsd Ft, offset(Xn) + Emit32((WORD)(0x00003027 | (Xn << 15) | (Ft1 << 20) | (offset & 0xF) << 7 | ((offset >> 4) & 0xFF))); + // fsd Ft, (offset + 8)(Xn) + Emit32((WORD)(0x00003027 | (Xn << 15) | (Ft2 << 20) | ((offset + 8) & 0xF) << 7 | (((offset + 8) >> 4) & 0xFF))); + } +} + +void StubLinkerCPU::EmitLoadStoreRegImm(DWORD flags, IntReg Xt, IntReg Xn, int offset) +{ + BOOL isLoad = flags & 1; + if (isLoad) { + // ld regNum, offset(Xn); + Emit32((DWORD)(0x00003003 | (Xt << 7) | (Xn << 15) | (offset << 20))); + } else { + // sd regNum, offset(Xn) + Emit32((DWORD)(0x00003023 | (Xt << 20) | (Xn << 15) | (offset & 0xF) << 7 | (((offset >> 4) & 0xFF) << 25))); + } +} + +void StubLinkerCPU::EmitLoadStoreRegImm(DWORD flags, FloatReg Ft, IntReg Xn, int offset) +{ + BOOL isLoad = flags & 1; + if (isLoad) { + // fld Ft, Xn, offset + Emit32((DWORD)(0x00003007 | (Xn << 15) | (Ft << 7) | (offset << 20))); + } else { + // fsd Ft, offset(Xn) + Emit32((WORD)(0x00003027 | (Xn << 15) | (Ft << 20) | (offset & 0xF) << 7 | ((offset >> 4) & 0xFF))); + } +} + +void StubLinkerCPU::EmitLoadFloatRegImm(FloatReg ft, IntReg base, int offset) +{ + // fld ft,base,offset + _ASSERTE(offset <= 2047 && offset >= -2048); + Emit32(0x2b800000 | (base.reg << 15) | ((offset & 0xfff)<<20) | (ft.reg << 7)); +} + +void StubLinkerCPU::EmitMovReg(IntReg Xd, IntReg Xm) +{ + Emit32(0x00000013 | (Xm << 15) | (Xd << 7)); +} + +void StubLinkerCPU::EmitSubImm(IntReg Xd, IntReg Xn, unsigned int value) +{ + _ASSERTE((0 <= value) && (value <= 0x7FF)); + Emit32((DWORD)(0x00000013 | (((~value + 0x1) & 0xFFF) << 20) | (Xn << 15) | (Xd << 7))); // addi Xd, Xn, (~value + 0x1) & 0xFFF +} + +void StubLinkerCPU::EmitAddImm(IntReg Xd, IntReg Xn, unsigned int value) +{ + _ASSERTE((0 <= value) && (value <= 0x7FF)); + Emit32((DWORD)(0x00000013 | (value << 20) | (Xn << 15) | (Xd << 7))); // addi Xd, Xn, value +} + +void StubLinkerCPU::EmitCallRegister(IntReg reg) +{ + _ASSERTE(!"RISCV64: not implementation on riscv64!!!"); +} + +void StubLinkerCPU::Init() +{ + new (gBranchIF) BranchInstructionFormat(); +} + +// Emits code to adjust arguments for static delegate target. +VOID StubLinkerCPU::EmitShuffleThunk(ShuffleEntry *pShuffleEntryArray) +{ + // On entry a0 holds the delegate instance. Look up the real target address stored in the MethodPtrAux + // field and saved in t6. Tailcall to the target method after re-arranging the arguments + // ld t6, a0, offsetof(DelegateObject, _methodPtrAux) + EmitLoadStoreRegImm(eLOAD, IntReg(31)/*t6*/, IntReg(10)/*a0*/, DelegateObject::GetOffsetOfMethodPtrAux()); + // addi t5, a0, DelegateObject::GetOffsetOfMethodPtrAux() - load the indirection cell into t5 used by ResolveWorkerAsmStub + EmitAddImm(30/*t5*/, 10/*a0*/, DelegateObject::GetOffsetOfMethodPtrAux()); + + int delay_index[8] = {-1}; + bool is_store = false; + UINT16 index = 0; + int i = 0; + for (ShuffleEntry* pEntry = pShuffleEntryArray; pEntry->srcofs != ShuffleEntry::SENTINEL; pEntry++, i++) + { + if (pEntry->srcofs & ShuffleEntry::REGMASK) + { + // Source in register, destination in register + + // Both the srcofs and dstofs must be of the same kind of registers - float or general purpose. + // If source is present in register then destination may be a stack-slot. + _ASSERTE(((pEntry->dstofs & ShuffleEntry::FPREGMASK) == (pEntry->srcofs & ShuffleEntry::FPREGMASK)) || !(pEntry->dstofs & (ShuffleEntry::FPREGMASK | ShuffleEntry::REGMASK))); + _ASSERTE((pEntry->dstofs & ShuffleEntry::OFSREGMASK) <= 8);//should amend for offset! + _ASSERTE((pEntry->srcofs & ShuffleEntry::OFSREGMASK) <= 8); + + if (pEntry->srcofs & ShuffleEntry::FPREGMASK) + { + _ASSERTE(!"RISCV64: not validated on riscv64!!!"); + // FirstFloatReg is 10; + int j = 10; + while (pEntry[j].srcofs & ShuffleEntry::FPREGMASK) + { + j++; + } + assert((pEntry->dstofs - pEntry->srcofs) == index); + assert(8 > index); + + int tmp_reg = 0; // f0. + ShuffleEntry* tmp_entry = pShuffleEntryArray + delay_index[0]; + while (index) + { + // fld(Ft, sp, offset); + _ASSERTE(isValidSimm12(tmp_entry->srcofs << 3)); + Emit32(0x3007 | (tmp_reg << 15) | (2 << 7/*sp*/) | ((tmp_entry->srcofs << 3) << 20)); + tmp_reg++; + index--; + tmp_entry++; + } + + j -= 1; + tmp_entry = pEntry + j; + i += j; + while (pEntry[j].srcofs & ShuffleEntry::FPREGMASK) + { + if (pEntry[j].dstofs & ShuffleEntry::FPREGMASK)// fsgnj.d fd, fs, fs + Emit32(0x22000053 | ((pEntry[j].dstofs & ShuffleEntry::OFSREGMASK) << 7) | ((pEntry[j].srcofs & ShuffleEntry::OFSREGMASK) << 15) | ((pEntry[j].srcofs & ShuffleEntry::OFSREGMASK) << 20)); + else //// fsd(Ft, Rn, offset); + { + _ASSERTE(isValidSimm12((pEntry[j].dstofs * sizeof(long)))); + Emit32(0x3027 | ((pEntry[j].srcofs & ShuffleEntry::OFSREGMASK) << 20) | (2 << 15 /*sp*/) | ((pEntry[j].dstofs * sizeof(long) & 0x1f) << 7) | ((pEntry[j].dstofs * sizeof(long) & 0x7f) << 25)); + } + j--; + } + assert(tmp_reg <= 11); + /* + while (tmp_reg > 11) + { + tmp_reg--; + // fmov.d fd, fs + Emit32(0x01149800 | index | (tmp_reg << 5)); + index++; + } + */ + index = 0; + pEntry = tmp_entry; + } + else + { + // 10 is the offset of FirstGenArgReg to FirstGenReg + assert(pEntry->dstofs & ShuffleEntry::REGMASK); + assert((pEntry->dstofs & ShuffleEntry::OFSMASK) < (pEntry->srcofs & ShuffleEntry::OFSMASK)); + EmitMovReg(IntReg((pEntry->dstofs & ShuffleEntry::OFSMASK) + 10), IntReg((pEntry->srcofs & ShuffleEntry::OFSMASK) + 10)); + } + } + else if (pEntry->dstofs & ShuffleEntry::REGMASK) + { + // source must be on the stack + _ASSERTE(!(pEntry->srcofs & ShuffleEntry::REGMASK)); + + if (pEntry->dstofs & ShuffleEntry::FPREGMASK) + { + if (!is_store) + { + delay_index[index++] = i; + continue; + } + EmitLoadFloatRegImm(FloatReg((pEntry->dstofs & ShuffleEntry::OFSREGMASK) + 10), RegSp, pEntry->srcofs * sizeof(void*)); + } + else + { + assert(pEntry->dstofs & ShuffleEntry::REGMASK); + EmitLoadStoreRegImm(eLOAD, IntReg((pEntry->dstofs & ShuffleEntry::OFSMASK) + 10), RegSp, pEntry->srcofs * sizeof(void*)); + } + } + else + { + // source must be on the stack + _ASSERTE(!(pEntry->srcofs & ShuffleEntry::REGMASK)); + + // dest must be on the stack + _ASSERTE(!(pEntry->dstofs & ShuffleEntry::REGMASK)); + + EmitLoadStoreRegImm(eLOAD, IntReg(29)/*t4*/, RegSp, pEntry->srcofs * sizeof(void*)); + EmitLoadStoreRegImm(eSTORE, IntReg(29)/*t4*/, RegSp, pEntry->dstofs * sizeof(void*)); + } + } + + // Tailcall to target + // jalr x0, 0(t6) + EmitJumpRegister(31); +} + +// Emits code to adjust arguments for static delegate target. +VOID StubLinkerCPU::EmitComputedInstantiatingMethodStub(MethodDesc* pSharedMD, struct ShuffleEntry *pShuffleEntryArray, void* extraArg) +{ + STANDARD_VM_CONTRACT; + + for (ShuffleEntry* pEntry = pShuffleEntryArray; pEntry->srcofs != ShuffleEntry::SENTINEL; pEntry++) + { + _ASSERTE(pEntry->dstofs & ShuffleEntry::REGMASK); + _ASSERTE(pEntry->srcofs & ShuffleEntry::REGMASK); + _ASSERTE(!(pEntry->dstofs & ShuffleEntry::FPREGMASK)); + _ASSERTE(!(pEntry->srcofs & ShuffleEntry::FPREGMASK)); + _ASSERTE(pEntry->dstofs != ShuffleEntry::HELPERREG); + _ASSERTE(pEntry->srcofs != ShuffleEntry::HELPERREG); + + EmitMovReg(IntReg((pEntry->dstofs & ShuffleEntry::OFSREGMASK) + 4), IntReg((pEntry->srcofs & ShuffleEntry::OFSREGMASK) + 4)); + } + + MetaSig msig(pSharedMD); + ArgIterator argit(&msig); + + if (argit.HasParamType()) + { + ArgLocDesc sInstArgLoc; + argit.GetParamTypeLoc(&sInstArgLoc); + int regHidden = sInstArgLoc.m_idxGenReg; + _ASSERTE(regHidden != -1); + regHidden += 10;//NOTE: RISCV64 should start at a0=10; + + if (extraArg == NULL) + { + if (pSharedMD->RequiresInstMethodTableArg()) + { + // Unboxing stub case + // Fill param arg with methodtable of this pointer + // ld regHidden, a0, 0 + EmitLoadStoreRegImm(eLOAD, IntReg(regHidden), IntReg(10), 0); + } + } + else + { + EmitMovConstant(IntReg(regHidden), (UINT64)extraArg); + } + } + + if (extraArg == NULL) + { + // Unboxing stub case + // Address of the value type is address of the boxed instance plus sizeof(MethodDesc*). + // addi a0, a0, sizeof(MethodDesc*) + EmitAddImm(IntReg(10), IntReg(10), sizeof(MethodDesc*)); + } + + // Tail call the real target. + EmitCallManagedMethod(pSharedMD, TRUE /* tail call */); + SetTargetMethod(pSharedMD); +} + +void StubLinkerCPU::EmitCallLabel(CodeLabel *target, BOOL fTailCall, BOOL fIndirect) +{ + BranchInstructionFormat::VariationCodes variationCode = BranchInstructionFormat::VariationCodes::BIF_VAR_JUMP; + if (!fTailCall) + variationCode = static_cast(variationCode | BranchInstructionFormat::VariationCodes::BIF_VAR_CALL); + if (fIndirect) + variationCode = static_cast(variationCode | BranchInstructionFormat::VariationCodes::BIF_VAR_INDIRECT); + + EmitLabelRef(target, reinterpret_cast(gBranchIF), (UINT)variationCode); +} + +void StubLinkerCPU::EmitCallManagedMethod(MethodDesc *pMD, BOOL fTailCall) +{ + // Use direct call if possible. + if (pMD->HasStableEntryPoint()) + { + EmitCallLabel(NewExternalCodeLabel((LPVOID)pMD->GetStableEntryPoint()), fTailCall, FALSE); + } + else + { + EmitCallLabel(NewExternalCodeLabel((LPVOID)pMD->GetAddrOfSlot()), fTailCall, TRUE); + } +} + + +#ifdef FEATURE_READYTORUN + +// +// Allocation of dynamic helpers +// + +#define DYNAMIC_HELPER_ALIGNMENT sizeof(TADDR) + +#define BEGIN_DYNAMIC_HELPER_EMIT(size) \ + _ASSERTE(!"RISCV64: not implementation on riscv64!!!"); +#define END_DYNAMIC_HELPER_EMIT() \ + _ASSERTE(!"RISCV64: not implementation on riscv64!!!"); + +// Uses x8 as scratch register to store address of data label +// After load x8 is increment to point to next data +// only accepts positive offsets +static void LoadRegPair(BYTE* p, int reg1, int reg2, UINT32 offset) +{ + _ASSERTE(!"RISCV64: not implementation on riscv64!!!"); +} + +PCODE DynamicHelpers::CreateHelper(LoaderAllocator * pAllocator, TADDR arg, PCODE target) +{ + _ASSERTE(!"RISCV64: not implementation on riscv64!!!"); + return NULL; +} + +// Caller must ensure sufficient byte are allocated including padding (if applicable) +void DynamicHelpers::EmitHelperWithArg(BYTE*& p, size_t rxOffset, LoaderAllocator * pAllocator, TADDR arg, PCODE target) +{ + _ASSERTE(!"RISCV64: not implementation on riscv64!!!"); +} + +PCODE DynamicHelpers::CreateHelperWithArg(LoaderAllocator * pAllocator, TADDR arg, PCODE target) +{ + _ASSERTE(!"RISCV64: not implementation on riscv64!!!"); + return NULL; +} + +PCODE DynamicHelpers::CreateHelper(LoaderAllocator * pAllocator, TADDR arg, TADDR arg2, PCODE target) +{ + _ASSERTE(!"RISCV64: not implementation on riscv64!!!"); + return NULL; +} + +PCODE DynamicHelpers::CreateHelperArgMove(LoaderAllocator * pAllocator, TADDR arg, PCODE target) +{ + _ASSERTE(!"RISCV64: not implementation on riscv64!!!"); + return NULL; +} + +PCODE DynamicHelpers::CreateReturn(LoaderAllocator * pAllocator) +{ + _ASSERTE(!"RISCV64: not implementation on riscv64!!!"); + return NULL; +} + +PCODE DynamicHelpers::CreateReturnConst(LoaderAllocator * pAllocator, TADDR arg) +{ + _ASSERTE(!"RISCV64: not implementation on riscv64!!!"); + return NULL; +} + +PCODE DynamicHelpers::CreateReturnIndirConst(LoaderAllocator * pAllocator, TADDR arg, INT8 offset) +{ + _ASSERTE(!"RISCV64: not implementation on riscv64!!!"); + return NULL; +} + +PCODE DynamicHelpers::CreateHelperWithTwoArgs(LoaderAllocator * pAllocator, TADDR arg, PCODE target) +{ + _ASSERTE(!"RISCV64: not implementation on riscv64!!!"); + return NULL; +} + +PCODE DynamicHelpers::CreateHelperWithTwoArgs(LoaderAllocator * pAllocator, TADDR arg, TADDR arg2, PCODE target) +{ + _ASSERTE(!"RISCV64: not implementation on riscv64!!!"); + return NULL; +} + +PCODE DynamicHelpers::CreateDictionaryLookupHelper(LoaderAllocator * pAllocator, CORINFO_RUNTIME_LOOKUP * pLookup, DWORD dictionaryIndexAndSlot, Module * pModule) +{ + _ASSERTE(!"RISCV64: not implementation on riscv64!!!"); + return NULL; +} +#endif // FEATURE_READYTORUN + + +#endif // #ifndef DACCESS_COMPILE diff --git a/src/coreclr/vm/riscv64/thunktemplates.S b/src/coreclr/vm/riscv64/thunktemplates.S index a7cd5b6c4d2403..e078b1a7b989f3 100644 --- a/src/coreclr/vm/riscv64/thunktemplates.S +++ b/src/coreclr/vm/riscv64/thunktemplates.S @@ -4,4 +4,34 @@ #include "unixasmmacros.inc" #include "asmconstants.h" -#error "TODO-RISCV64: missing implementation" +LEAF_ENTRY StubPrecodeCode + auipc t1, 0x1 + ld t2, (StubPrecodeData__MethodDesc)(t1) + ld t1, (StubPrecodeData__Target)(t1) + jalr x0,t1,0 +LEAF_END_MARKED StubPrecodeCode + +LEAF_ENTRY FixupPrecodeCode + auipc t2, 0x1 + ld t2, (FixupPrecodeData__Target)(t2) + c.jr t2 + + auipc t2, 0x1 + ld t1, (FixupPrecodeData__PrecodeFixupThunk - 0xa)(t2) + ld t2, (FixupPrecodeData__MethodDesc - 0xa)(t2) + jalr x0, t1, 0 +LEAF_END_MARKED FixupPrecodeCode + +LEAF_ENTRY CallCountingStubCode + auipc t2, 0x1 + ld t3, (CallCountingStubData__RemainingCallCountCell)(t2) + lh t1, 0(t3) + addiw t1, t1, -1 + sh t1, 0(t3) + beq t1, zero, LOCAL_LABEL(CountReachedZero) + ld t1, (CallCountingStubData__TargetForMethod)(t2) + jalr x0, 0(t1) +LOCAL_LABEL(CountReachedZero): + ld t1, (CallCountingStubData__TargetForThresholdReached)(t2) + jalr x0,t1,0 +LEAF_END_MARKED CallCountingStubCode diff --git a/src/coreclr/vm/riscv64/virtualcallstubcpu.hpp b/src/coreclr/vm/riscv64/virtualcallstubcpu.hpp index 9a0cdd4e406d37..688c6eac727ec9 100644 --- a/src/coreclr/vm/riscv64/virtualcallstubcpu.hpp +++ b/src/coreclr/vm/riscv64/virtualcallstubcpu.hpp @@ -1,4 +1,567 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +// +// VirtualCallStubCpu.hpp +// +#ifndef _VIRTUAL_CALL_STUB_ARM_H +#define _VIRTUAL_CALL_STUB_ARM_H -#error "TODO-RISCV64: missing implementation" +#define DISPATCH_STUB_FIRST_DWORD 0x00000e97 +#define RESOLVE_STUB_FIRST_DWORD 0x00053e03 +#define VTABLECALL_STUB_FIRST_DWORD 0x00053e83 + +#define LOOKUP_STUB_FIRST_DWORD 0x00000f97 + +#define USES_LOOKUP_STUBS 1 + +struct LookupStub +{ + inline PCODE entryPoint() { LIMITED_METHOD_CONTRACT; return (PCODE)&_entryPoint[0]; } + inline size_t token() { LIMITED_METHOD_CONTRACT; return _token; } + inline size_t size() { LIMITED_METHOD_CONTRACT; return sizeof(LookupStub); } +private : + friend struct LookupHolder; + + DWORD _entryPoint[4]; + PCODE _resolveWorkerTarget; + size_t _token; +}; + +struct LookupHolder +{ +private: + LookupStub _stub; +public: + static void InitializeStatic() { } + + void Initialize(LookupHolder* pLookupHolderRX, PCODE resolveWorkerTarget, size_t dispatchToken) + { + // auipc t6, 0 + // ld t2, (12 + 12)(ra) + // ld t6, (4 + 12)(ra) + // jalr x0, t6, 0 + // + // _resolveWorkerTarget + // _token + + _stub._entryPoint[0] = LOOKUP_STUB_FIRST_DWORD; // auipc t6, 0 //0x00000097 + _stub._entryPoint[1] = 0x018fb383; //ld t2, 24(t6) + _stub._entryPoint[2] = 0x010fbf83; //ld t6, 16(t6) + _stub._entryPoint[3] = 0x000f8067; //jalr x0, t6, 0 + + _stub._resolveWorkerTarget = resolveWorkerTarget; + _stub._token = dispatchToken; + } + + LookupStub* stub() { LIMITED_METHOD_CONTRACT; return &_stub; } + static LookupHolder* FromLookupEntry(PCODE lookupEntry) + { + return (LookupHolder*) ( lookupEntry - offsetof(LookupHolder, _stub) - offsetof(LookupStub, _entryPoint) ); + } +}; + +struct DispatchStub +{ + inline PCODE entryPoint() { LIMITED_METHOD_CONTRACT; return (PCODE)&_entryPoint[0]; } + + inline size_t expectedMT() { LIMITED_METHOD_CONTRACT; return _expectedMT; } + inline PCODE implTarget() { LIMITED_METHOD_CONTRACT; return _implTarget; } + + inline TADDR implTargetSlot(EntryPointSlots::SlotType *slotTypeRef) const + { + LIMITED_METHOD_CONTRACT; + _ASSERTE(slotTypeRef != nullptr); + + *slotTypeRef = EntryPointSlots::SlotType_Executable; + return (TADDR)&_implTarget; + } + + inline PCODE failTarget() { LIMITED_METHOD_CONTRACT; return _failTarget; } + inline size_t size() { LIMITED_METHOD_CONTRACT; return sizeof(DispatchStub); } + +private: + friend struct DispatchHolder; + + DWORD _entryPoint[9]; + DWORD _pad; + size_t _expectedMT; + PCODE _implTarget; + PCODE _failTarget; +}; + +struct DispatchHolder +{ + static void InitializeStatic() + { + LIMITED_METHOD_CONTRACT; + + // Check that _implTarget is aligned in the DispatchHolder for backpatching + static_assert_no_msg(((offsetof(DispatchHolder, _stub) + offsetof(DispatchStub, _implTarget)) % sizeof(void *)) == 0); + } + + void Initialize(DispatchHolder* pDispatchHolderRX, PCODE implTarget, PCODE failTarget, size_t expectedMT) + { + // auipc t4,0 + // addi t4, t4, 36 + // ld t0,0(a0) ; methodTable from object in $a0 + // ld t6,0(t4) // t6 _expectedMT + // bne t6, t0, failLabel + // ld t4, 8(t4) // t4 _implTarget + // jalr x0, t4, 0 + // failLabel: + // ld t4, 16(t4) // t4 _failTarget + // jalr x0, t4, 0 + // + // + // _expectedMT + // _implTarget + // _failTarget + + _stub._entryPoint[0] = DISPATCH_STUB_FIRST_DWORD; // auipc t4,0 // 0x00000e97 + _stub._entryPoint[1] = 0x028e8e93; // addi t4, t4, 40 + _stub._entryPoint[2] = 0x00053283; // ld t0, 0(a0) //; methodTable from object in $a0 + _stub._entryPoint[3] = 0x000ebf83; // ld r6, 0(t4) // t6 _expectedMT + _stub._entryPoint[4] = 0x005f9663; // bne t6, t0, failLabel + _stub._entryPoint[5] = 0x008ebe83; // ld t4, 8(t4) // t4 _implTarget + _stub._entryPoint[6] = 0x000e8067; // jalr x0, t4, 0 + _stub._entryPoint[7] = 0x010ebe83; // ld t4, 16(t4) // t4 _failTarget + _stub._entryPoint[8] = 0x000e8067; // jalr x0, t4, 0 + + _stub._expectedMT = expectedMT; + _stub._implTarget = implTarget; + _stub._failTarget = failTarget; + } + + DispatchStub* stub() { LIMITED_METHOD_CONTRACT; return &_stub; } + + static DispatchHolder* FromDispatchEntry(PCODE dispatchEntry) + { + LIMITED_METHOD_CONTRACT; + DispatchHolder* dispatchHolder = (DispatchHolder*) ( dispatchEntry - offsetof(DispatchHolder, _stub) - offsetof(DispatchStub, _entryPoint) ); + return dispatchHolder; + } + +private: + DispatchStub _stub; +}; + +struct ResolveStub +{ + inline PCODE failEntryPoint() { LIMITED_METHOD_CONTRACT; return (PCODE)&_failEntryPoint[0]; } + inline PCODE resolveEntryPoint() { LIMITED_METHOD_CONTRACT; return (PCODE)&_resolveEntryPoint[0]; } + inline PCODE slowEntryPoint() { LIMITED_METHOD_CONTRACT; return (PCODE)&_slowEntryPoint[0]; } + inline size_t token() { LIMITED_METHOD_CONTRACT; return _token; } + inline INT32* pCounter() { LIMITED_METHOD_CONTRACT; return _pCounter; } + + inline UINT32 hashedToken() { LIMITED_METHOD_CONTRACT; return _hashedToken >> LOG2_PTRSIZE; } + inline size_t cacheAddress() { LIMITED_METHOD_CONTRACT; return _cacheAddress; } + inline size_t size() { LIMITED_METHOD_CONTRACT; return sizeof(ResolveStub); } + +private: + friend struct ResolveHolder; + const static int resolveEntryPointLen = 20; // TODO RISCV64 + const static int slowEntryPointLen = 4; + const static int failEntryPointLen = 9; + + DWORD _resolveEntryPoint[resolveEntryPointLen]; + DWORD _slowEntryPoint[slowEntryPointLen]; + DWORD _failEntryPoint[failEntryPointLen]; + UINT32 _hashedToken; + INT32* _pCounter; //Base of the Data Region + size_t _cacheAddress; // lookupCache + size_t _token; + PCODE _resolveWorkerTarget; +}; + +struct ResolveHolder +{ + static void InitializeStatic() { } + + void Initialize(ResolveHolder* pResolveHolderRX, + PCODE resolveWorkerTarget, PCODE patcherTarget, + size_t dispatchToken, UINT32 hashedToken, + void * cacheAddr, INT32 * counterAddr) + { + int n=0; + INT32 pc_offset; + +/******** Rough Convention of used in this routine + ;;ra temp base address of loading data region + ;;t5 indirection cell // TODO CHECK t8 => t5 + ;;t3 MethodTable (from object ref in a0), out: this._token + ;;t0 hash scratch + ;;t1 temp + ;;t2 temp + ;;t6 hash scratch // TODO CHECK R21 => t6 + ;;cachemask => [CALL_STUB_CACHE_MASK * sizeof(void*)] + + // Called directly by JITTED code + // ResolveStub._resolveEntryPoint(a0:Object*, a1 ...,a7, t8:IndirectionCellAndFlags) + // { + // MethodTable mt = a0.m_pMethTab; + // int i = ((mt + mt >> 12) ^ this._hashedToken) & _cacheMask + // ResolveCacheElem e = this._cacheAddress + i + // t1 = e = this._cacheAddress + i + // if (mt == e.pMT && this._token == e.token) + // { + // (e.target)(a0, [a1,...,a7]); + // } + // else + // { + // t3 = this._token; + // (this._slowEntryPoint)(a0, [a1,.., a7], t5, t3); + // } + // } + ********/ + + ///;;resolveEntryPoint + // Called directly by JITTED code + // ResolveStub._resolveEntryPoint(a0:Object*, a1 ...,a7, t5:IndirectionCellAndFlags) + + // ld t3, 0(a0) + _stub._resolveEntryPoint[n++] = RESOLVE_STUB_FIRST_DWORD; + // srli t0, t3, 0xc + _stub._resolveEntryPoint[n++] = 0x00ce5293; + // add t1, t3, t0 + _stub._resolveEntryPoint[n++] = 0x005e0333; + // auipc t0, 0 + _stub._resolveEntryPoint[n++] = 0x00000297; + // addi t0, t0, -16 + _stub._resolveEntryPoint[n++] = 0xff428293; + + // lw t6, 0(t0) #t6 = this._hashedToken + _stub._resolveEntryPoint[n++] = 0x0002af83 | (33 << 22); //(20+4+9)*4<<20; + _ASSERTE((ResolveStub::resolveEntryPointLen+ResolveStub::slowEntryPointLen+ResolveStub::failEntryPointLen) == 33); + _ASSERTE((33<<2) == (offsetof(ResolveStub, _hashedToken) -offsetof(ResolveStub, _resolveEntryPoint[0]))); + + // xor t1, t1, t6 + _stub._resolveEntryPoint[n++] = 0x01f34333; + // cachemask + _ASSERTE(CALL_STUB_CACHE_MASK * sizeof(void*) == 0x7ff8); + // lui t6, 0x7ff8 + _stub._resolveEntryPoint[n++] = 0x07ff8fb7; + // srliw t6, t6, 12 + _stub._resolveEntryPoint[n++] = 0x00cfdf9b; + // and t1, t1, t6 + _stub._resolveEntryPoint[n++] = 0x01f37333; + // ld t6, 0(t0) # t6 = this._cacheAddress + _stub._resolveEntryPoint[n++] = 0x0002bf83 | (36 << 22); //(20+4+9+1+2)*4<<20; + _ASSERTE((ResolveStub::resolveEntryPointLen+ResolveStub::slowEntryPointLen+ResolveStub::failEntryPointLen+1+2) == 36); + _ASSERTE((36<<2) == (offsetof(ResolveStub, _cacheAddress) -offsetof(ResolveStub, _resolveEntryPoint[0]))); + // add t1, t6, t1 + _stub._resolveEntryPoint[n++] = 0x006f8333; + // ld t1, 0(t1) # t1 = e = this._cacheAddress[i] + _stub._resolveEntryPoint[n++] = 0x00033303; + + // ld t6, 0(t1) # t6 = Check mt == e.pMT; + _stub._resolveEntryPoint[n++] = 0x00033f83 | ((offsetof(ResolveCacheElem, pMT) & 0xfff) << 20); + // ld t2, 0(t0) # $t2 = this._token + _stub._resolveEntryPoint[n++] = 0x0002b383 | (38<<22);//(20+4+9+1+2+2)*4<<20; + _ASSERTE((ResolveStub::resolveEntryPointLen+ResolveStub::slowEntryPointLen+ResolveStub::failEntryPointLen+1+4) == 38); + _ASSERTE((38<<2) == (offsetof(ResolveStub, _token) -offsetof(ResolveStub, _resolveEntryPoint[0]))); + + // bne t6, t3, next + _stub._resolveEntryPoint[n++] = 0x01cf9a63;// | PC_REL_OFFSET(_slowEntryPoint[0], n); + + // ld t6, 0(t1) # t6 = e.token; + _stub._resolveEntryPoint[n++] = 0x00033f83 | ((offsetof(ResolveCacheElem, token) & 0xfff)<<10); + // bne t6, t2, next + _stub._resolveEntryPoint[n++] = 0x007f9663;// | PC_REL_OFFSET(_slowEntryPoint[0], n); + + pc_offset = offsetof(ResolveCacheElem, target) & 0xffffffff; + _ASSERTE(pc_offset >=0 && pc_offset%8 == 0); + // ld t3, 0(t1) # t3 = e.target; + _stub._resolveEntryPoint[n++] = 0x00033e03 | ((offsetof(ResolveCacheElem, target) & 0xfff)<<10); + // jalr x0, t3, 0 + _stub._resolveEntryPoint[n++] = 0x000e0067; + + _ASSERTE(n == ResolveStub::resolveEntryPointLen); + _ASSERTE(_stub._resolveEntryPoint + n == _stub._slowEntryPoint); + + // ResolveStub._slowEntryPoint(a0:MethodToken, [a1..a7], t5:IndirectionCellAndFlags) + // { + // t2 = this._token; + // this._resolveWorkerTarget(a0, [a1..a7], t5, t2); + // } +//#undef PC_REL_OFFSET +//#define PC_REL_OFFSET(_member, _index) (((INT32)(offsetof(ResolveStub, _member) - (offsetof(ResolveStub, _slowEntryPoint[_index])))) & 0xffff) + // ;;slowEntryPoint: + // ;;fall through to the slow case + + // auipc t0, 0 + _stub._slowEntryPoint[0] = 0x00000297; + // ld t6, 0(t0) # r21 = _resolveWorkerTarget; + _ASSERTE((0x14*4) == ((INT32)(offsetof(ResolveStub, _resolveWorkerTarget) - (offsetof(ResolveStub, _slowEntryPoint[0]))))); + _ASSERTE((ResolveStub::slowEntryPointLen + ResolveStub::failEntryPointLen+1+3*2) == 0x14); + _stub._slowEntryPoint[1] = 0x0002bf83 | ((0x14 * 4) << 20); + + // ld t2, 0(t0) # t2 = this._token; + _stub._slowEntryPoint[2] = 0x0002b383 | ((0x12 * 4) << 20); //(18*4=72=0x48)<<20 + _ASSERTE((ResolveStub::slowEntryPointLen+ResolveStub::failEntryPointLen+1+4)*4 == (0x12 * 4)); + _ASSERTE((0x12 * 4) == (offsetof(ResolveStub, _token) -offsetof(ResolveStub, _slowEntryPoint[0]))); + + // jalr x0, t6, 0 + _stub._slowEntryPoint[3] = 0x000f8067; + + _ASSERTE(4 == ResolveStub::slowEntryPointLen); + + // ResolveStub._failEntryPoint(a0:MethodToken, a1,.., a7, t5:IndirectionCellAndFlags) + // { + // if(--*(this._pCounter) < 0) t5 = t5 | SDF_ResolveBackPatch; + // this._resolveEntryPoint(a0, [a1..a7]); + // } +//#undef PC_REL_OFFSET +//#define PC_REL_OFFSET(_member, _index) (((INT32)(offsetof(ResolveStub, _member) - (offsetof(ResolveStub, _failEntryPoint[_index])))) & 0xffff) + //;;failEntryPoint + + // auipc t0, 0 + _stub._failEntryPoint[0] = 0x00000297; + // ld t1, 0(t0) # t1 = _pCounter; 0x2800000=((failEntryPointLen+1)*4)<<20. + _stub._failEntryPoint[1] = 0x0002b303 | 0x2800000; + _ASSERTE((((ResolveStub::failEntryPointLen+1)*4)<<20) == 0x2800000); + _ASSERTE((0x2800000>>20) == ((INT32)(offsetof(ResolveStub, _pCounter) - (offsetof(ResolveStub, _failEntryPoint[0]))))); + // lw t6, 0(t1) + _stub._failEntryPoint[2] = 0x00032f83; + // addi t6, t6, -1 + _stub._failEntryPoint[3] = 0xffff8f93; + + // sw t6, 0(t1) + _stub._failEntryPoint[4] = 0x01f32023; + + _ASSERTE(SDF_ResolveBackPatch == 0x1); + // ;; ori t5, t5, t6 >=0 ? SDF_ResolveBackPatch:0; + // slti t6, t6, 0 + _stub._failEntryPoint[5] = 0x000faf93; + // xori t6, t6, 1 + _stub._failEntryPoint[6] = 0x001fcf93; + // or t5, t5, t6 + _stub._failEntryPoint[7] = 0x01ff6f33; + + // j _resolveEntryPoint // pc - 128 = pc + 4 - resolveEntryPointLen * 4 - slowEntryPointLen * 4 - failEntryPointLen * 4; + _stub._failEntryPoint[8] = 0xf81ff06f; + + _ASSERTE(9 == ResolveStub::failEntryPointLen); + _stub._pCounter = counterAddr; + _stub._hashedToken = hashedToken << LOG2_PTRSIZE; + _stub._cacheAddress = (size_t) cacheAddr; + _stub._token = dispatchToken; + _stub._resolveWorkerTarget = resolveWorkerTarget; + + _ASSERTE(resolveWorkerTarget == (PCODE)ResolveWorkerChainLookupAsmStub); + _ASSERTE(patcherTarget == NULL); + +#undef DATA_OFFSET +#undef PC_REL_OFFSET +#undef Dataregionbase + } + + ResolveStub* stub() { LIMITED_METHOD_CONTRACT; return &_stub; } + + static ResolveHolder* FromFailEntry(PCODE failEntry); + static ResolveHolder* FromResolveEntry(PCODE resolveEntry); +private: + ResolveStub _stub; +}; + + +/*VTableCallStub************************************************************************************** +These are jump stubs that perform a vtable-base virtual call. These stubs assume that an object is placed +in the first argument register (this pointer). From there, the stub extracts the MethodTable pointer, followed by the +vtable pointer, and finally jumps to the target method at a given slot in the vtable. +*/ +struct VTableCallStub +{ + friend struct VTableCallHolder; + + inline size_t size() + { + _ASSERTE(!"RISCV64:NYI"); + return 0; + } + + inline PCODE entryPoint() const { LIMITED_METHOD_CONTRACT; return (PCODE)&_entryPoint[0]; } + + inline size_t token() + { + LIMITED_METHOD_CONTRACT; + DWORD slot = *(DWORD*)(reinterpret_cast(this) + size() - 4); + return DispatchToken::CreateDispatchToken(slot).To_SIZE_T(); + } + +private: + BYTE _entryPoint[0]; // Dynamically sized stub. See Initialize() for more details. +}; + +/* VTableCallHolders are the containers for VTableCallStubs, they provide for any alignment of +stubs as necessary. */ +struct VTableCallHolder +{ + void Initialize(unsigned slot); + + VTableCallStub* stub() { LIMITED_METHOD_CONTRACT; return reinterpret_cast(this); } + + static size_t GetHolderSize(unsigned slot) + { + STATIC_CONTRACT_WRAPPER; + unsigned offsetOfIndirection = MethodTable::GetVtableOffset() + MethodTable::GetIndexOfVtableIndirection(slot) * TARGET_POINTER_SIZE; + unsigned offsetAfterIndirection = MethodTable::GetIndexAfterVtableIndirection(slot) * TARGET_POINTER_SIZE; + int indirectionsCodeSize = (offsetOfIndirection >= 0x1000 ? 12 : 4) + (offsetAfterIndirection >= 0x1000 ? 12 : 4); + int indirectionsDataSize = (offsetOfIndirection >= 0x1000 ? 4 : 0) + (offsetAfterIndirection >= 0x1000 ? 4 : 0); + return 12 + indirectionsCodeSize + ((indirectionsDataSize > 0) ? (indirectionsDataSize + 4) : 0); + } + + static VTableCallHolder* FromVTableCallEntry(PCODE entry) { LIMITED_METHOD_CONTRACT; return (VTableCallHolder*)entry; } + +private: + // VTableCallStub follows here. It is dynamically sized on allocation because it could + // use short/long instruction sizes for LDR, depending on the slot value. +}; + + +#ifdef DECLARE_DATA + +#ifndef DACCESS_COMPILE +ResolveHolder* ResolveHolder::FromFailEntry(PCODE failEntry) +{ + LIMITED_METHOD_CONTRACT; + ResolveHolder* resolveHolder = (ResolveHolder*) ( failEntry - offsetof(ResolveHolder, _stub) - offsetof(ResolveStub, _failEntryPoint) ); + return resolveHolder; +} + +ResolveHolder* ResolveHolder::FromResolveEntry(PCODE resolveEntry) +{ + LIMITED_METHOD_CONTRACT; + ResolveHolder* resolveHolder = (ResolveHolder*) ( resolveEntry - offsetof(ResolveHolder, _stub) - offsetof(ResolveStub, _resolveEntryPoint) ); + return resolveHolder; +} + +void VTableCallHolder::Initialize(unsigned slot) +{ + unsigned offsetOfIndirection = MethodTable::GetVtableOffset() + MethodTable::GetIndexOfVtableIndirection(slot) * TARGET_POINTER_SIZE; + unsigned offsetAfterIndirection = MethodTable::GetIndexAfterVtableIndirection(slot) * TARGET_POINTER_SIZE; + + VTableCallStub* pStub = stub(); + BYTE* p = (BYTE*)pStub->entryPoint(); + + // ld t4, 0(a0) : t4 = MethodTable pointer + *(UINT32*)p = 0x00053e83; // VTABLECALL_STUB_FIRST_DWORD + p += 4; + + if ((offsetOfIndirection >= 0x1000) || (offsetAfterIndirection >= 0x1000)) + { + *(UINT32*)p = 0x00000317; // auipc t1, 0 + p += 4; + } + + if (offsetOfIndirection >= 0x1000) + { + uint dataOffset = 20 + (offsetAfterIndirection >= 0x1000 ? 12 : 4); + + // lwu t3,dataOffset(t1) + *(DWORD*)p = 0x00036e03 | ((UINT32)dataOffset << 20); p += 4; + // add t4, t4, t3 + *(DWORD*)p = 0x01ce8eb3; p += 4; + // ld t4, offsetOfIndirection(t4) + *(DWORD*)p = 0x000ebe83; p += 4; + } + else + { + // ld t4, offsetOfIndirection(t4) + *(DWORD*)p = 0x000ebe83 | ((UINT32)offsetOfIndirection << 20); p += 4; + } + + if (offsetAfterIndirection >= 0x1000) + { + uint indirectionsCodeSize = (offsetOfIndirection >= 0x1000 ? 12 : 4); + uint indirectionsDataSize = (offsetOfIndirection >= 0x1000 ? 4 : 0); + uint dataOffset = 20 + indirectionsCodeSize + indirectionsDataSize; + + // ldw t3,dataOffset(t1) + *(DWORD*)p = 0x00036e03 | ((UINT32)dataOffset << 20); p += 4; + // add t4, t4, t3 + *(DWORD*)p = 0x01ce8eb3; p += 4; + // ld t4, 0(t4) + *(DWORD*)p = 0x000ebe83; p += 4; + } + else + { + // ld t4, offsetOfIndirection(t4) + *(DWORD*)p = 0x000ebe83 | ((UINT32)offsetAfterIndirection << 20); p += 4; + } + + // jalr x0, t4, 0 + *(UINT32*)p = 0x000e8067; p += 4; + + // data labels: + if (offsetOfIndirection >= 0x1000) + { + *(UINT32*)p = (UINT32)offsetOfIndirection; + p += 4; + } + if (offsetAfterIndirection >= 0x1000) + { + *(UINT32*)p = (UINT32)offsetAfterIndirection; + p += 4; + } + + // Store the slot value here for convenience. Not a real instruction (unreachable anyways) + // NOTE: Not counted in codeSize above. + *(UINT32*)p = slot; p += 4; + + _ASSERT(p == (BYTE*)stub()->entryPoint() + VTableCallHolder::GetHolderSize(slot)); + _ASSERT(stub()->size() == VTableCallHolder::GetHolderSize(slot)); +} + +#endif // DACCESS_COMPILE + +VirtualCallStubManager::StubKind VirtualCallStubManager::predictStubKind(PCODE stubStartAddress) +{ + SUPPORTS_DAC; +#ifdef DACCESS_COMPILE + + return SK_BREAKPOINT; // Dac always uses the slower lookup + +#else + + StubKind stubKind = SK_UNKNOWN; + TADDR pInstr = PCODEToPINSTR(stubStartAddress); + + EX_TRY + { + // If stubStartAddress is completely bogus, then this might AV, + // so we protect it with SEH. An AV here is OK. + AVInRuntimeImplOkayHolder AVOkay; + + DWORD firstDword = *((DWORD*) pInstr); + + if (firstDword == DISPATCH_STUB_FIRST_DWORD) // assembly of first instruction of DispatchStub : + { + stubKind = SK_DISPATCH; + } + else if (firstDword == RESOLVE_STUB_FIRST_DWORD) // assembly of first instruction of ResolveStub : + { + stubKind = SK_RESOLVE; + } + else if (firstDword == VTABLECALL_STUB_FIRST_DWORD) // assembly of first instruction of VTableCallStub : + { + stubKind = SK_VTABLECALL; + } + else if (firstDword == LOOKUP_STUB_FIRST_DWORD) // first instruction of LookupStub : + { + stubKind = SK_LOOKUP; + } + } + EX_CATCH + { + stubKind = SK_UNKNOWN; + } + EX_END_CATCH(SwallowAllExceptions); + + return stubKind; + +#endif // DACCESS_COMPILE +} + +#endif //DECLARE_DATA + +#endif // _VIRTUAL_CALL_STUB_ARM_H diff --git a/src/coreclr/vm/stackwalk.cpp b/src/coreclr/vm/stackwalk.cpp index 6b6d8c10f29daf..6abe943c47b4c4 100644 --- a/src/coreclr/vm/stackwalk.cpp +++ b/src/coreclr/vm/stackwalk.cpp @@ -303,7 +303,7 @@ bool CrawlFrame::IsGcSafe() return GetCodeManager()->IsGcSafe(&codeInfo, GetRelOffset()); } -#if defined(TARGET_ARM) || defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64) +#if defined(TARGET_ARM) || defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64) bool CrawlFrame::HasTailCalls() { CONTRACTL { @@ -314,7 +314,7 @@ bool CrawlFrame::HasTailCalls() return GetCodeManager()->HasTailCalls(&codeInfo); } -#endif // TARGET_ARM || TARGET_ARM64 || TARGET_LOONGARCH64 +#endif // TARGET_ARM || TARGET_ARM64 || TARGET_LOONGARCH64 || TARGET_RISCV64 inline void CrawlFrame::GotoNextFrame() { @@ -647,7 +647,7 @@ PCODE Thread::VirtualUnwindLeafCallFrame(T_CONTEXT* pContext) uControlPc = TADDR(pContext->Lr); -#elif defined(TARGET_LOONGARCH64) +#elif defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64) uControlPc = TADDR(pContext->Ra); #else diff --git a/src/coreclr/vm/stackwalk.h b/src/coreclr/vm/stackwalk.h index dcdd0bc79701fb..a85c85650a5778 100644 --- a/src/coreclr/vm/stackwalk.h +++ b/src/coreclr/vm/stackwalk.h @@ -305,9 +305,9 @@ class CrawlFrame */ bool IsGcSafe(); -#if defined(TARGET_ARM) || defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64) +#if defined(TARGET_ARM) || defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64) bool HasTailCalls(); -#endif // TARGET_ARM || TARGET_ARM64 || TARGET_LOONGARCH64 +#endif // TARGET_ARM || TARGET_ARM64 || TARGET_LOONGARCH64 || TARGET_RISCV64 PREGDISPLAY GetRegisterSet() { diff --git a/src/coreclr/vm/stublink.cpp b/src/coreclr/vm/stublink.cpp index 58048bfb1ab8c2..db228659c39774 100644 --- a/src/coreclr/vm/stublink.cpp +++ b/src/coreclr/vm/stublink.cpp @@ -1891,7 +1891,6 @@ UINT StubLinker::GetStackFrameSize() return m_cbStackSpace + (2 + m_cCalleeSavedRegs + m_cIntRegArgs + m_cVecRegArgs)*sizeof(void*); } - #endif // ifdef TARGET_ARM, elif defined(TARGET_ARM64) #endif // #ifndef DACCESS_COMPILE diff --git a/src/coreclr/vm/stubmgr.cpp b/src/coreclr/vm/stubmgr.cpp index 423465536c627c..ee022ce6713170 100644 --- a/src/coreclr/vm/stubmgr.cpp +++ b/src/coreclr/vm/stubmgr.cpp @@ -1830,7 +1830,7 @@ static BOOL IsVarargPInvokeStub(PCODE stubStartAddress) if (stubStartAddress == GetEEFuncEntryPoint(VarargPInvokeStub)) return TRUE; -#if !defined(TARGET_X86) && !defined(TARGET_ARM64) && !defined(TARGET_LOONGARCH64) +#if !defined(TARGET_X86) && !defined(TARGET_ARM64) && !defined(TARGET_LOONGARCH64) && !defined(TARGET_RISCV64) if (stubStartAddress == GetEEFuncEntryPoint(VarargPInvokeStub_RetBuffArg)) return TRUE; #endif diff --git a/src/coreclr/vm/threads.cpp b/src/coreclr/vm/threads.cpp index fcb79dc0f8eca6..2dd6b621599728 100644 --- a/src/coreclr/vm/threads.cpp +++ b/src/coreclr/vm/threads.cpp @@ -1114,11 +1114,11 @@ extern "C" void *JIT_WriteBarrier_Loc; void *JIT_WriteBarrier_Loc = 0; #endif -#if defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64) +#if defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64) extern "C" void (*JIT_WriteBarrier_Table)(); extern "C" void *JIT_WriteBarrier_Table_Loc; void *JIT_WriteBarrier_Table_Loc = 0; -#endif // TARGET_ARM64 || TARGET_LOONGARCH64 +#endif // TARGET_ARM64 || TARGET_LOONGARCH64 || TARGET_RISCV64 #ifndef TARGET_UNIX // g_TlsIndex is only used by the DAC. Disable optimizations around it to prevent it from getting optimized out. @@ -1182,17 +1182,17 @@ void InitThreadManager() SetJitHelperFunction(CORINFO_HELP_ASSIGN_REF, GetWriteBarrierCodeLocation((void*)JIT_WriteBarrier)); ETW::MethodLog::StubInitialized((ULONGLONG)GetWriteBarrierCodeLocation((void*)JIT_WriteBarrier), W("@WriteBarrier")); -#if defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64) +#if defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64) // Store the JIT_WriteBarrier_Table copy location to a global variable so that it can be updated. JIT_WriteBarrier_Table_Loc = GetWriteBarrierCodeLocation((void*)&JIT_WriteBarrier_Table); -#endif // TARGET_ARM64 || TARGET_LOONGARCH64 +#endif // TARGET_ARM64 || TARGET_LOONGARCH64 || TARGET_RISCV64 -#if defined(TARGET_ARM64) || defined(TARGET_ARM) || defined(TARGET_LOONGARCH64) +#if defined(TARGET_ARM64) || defined(TARGET_ARM) || defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64) SetJitHelperFunction(CORINFO_HELP_CHECKED_ASSIGN_REF, GetWriteBarrierCodeLocation((void*)JIT_CheckedWriteBarrier)); ETW::MethodLog::StubInitialized((ULONGLONG)GetWriteBarrierCodeLocation((void*)JIT_CheckedWriteBarrier), W("@CheckedWriteBarrier")); SetJitHelperFunction(CORINFO_HELP_ASSIGN_BYREF, GetWriteBarrierCodeLocation((void*)JIT_ByRefWriteBarrier)); ETW::MethodLog::StubInitialized((ULONGLONG)GetWriteBarrierCodeLocation((void*)JIT_ByRefWriteBarrier), W("@ByRefWriteBarrier")); -#endif // TARGET_ARM64 || TARGET_ARM || TARGET_LOONGARCH64 +#endif // TARGET_ARM64 || TARGET_ARM || TARGET_LOONGARCH64 || TARGET_RISCV64 } else @@ -1216,10 +1216,10 @@ void InitThreadManager() #else JIT_WriteBarrier_Loc = (void*)JIT_WriteBarrier; #endif -#if defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64) +#if defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64) // Store the JIT_WriteBarrier_Table copy location to a global variable so that it can be updated. JIT_WriteBarrier_Table_Loc = (void*)&JIT_WriteBarrier_Table; -#endif // TARGET_ARM64 || TARGET_LOONGARCH64 +#endif // TARGET_ARM64 || TARGET_LOONGARCH64 || TARGET_RISCV64 } #ifndef TARGET_UNIX diff --git a/src/coreclr/vm/threadsuspend.cpp b/src/coreclr/vm/threadsuspend.cpp index 89f2f9d33f7e2c..ffff61764fc97c 100644 --- a/src/coreclr/vm/threadsuspend.cpp +++ b/src/coreclr/vm/threadsuspend.cpp @@ -3722,7 +3722,7 @@ void Thread::CommitGCStressInstructionUpdate() else *(DWORD*)destCodeWriterHolder.GetRW() = *(DWORD*)pbSrcCode; -#elif defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64) +#elif defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64) *(DWORD*)destCodeWriterHolder.GetRW() = *(DWORD*)pbSrcCode; @@ -4881,7 +4881,7 @@ StackWalkAction SWCB_GetExecutionState(CrawlFrame *pCF, VOID *pData) { // We already have the caller context available at this point _ASSERTE(pRDT->IsCallerContextValid); -#if defined(TARGET_ARM) || defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64) +#if defined(TARGET_ARM) || defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64) // Why do we use CallerContextPointers below? // @@ -4900,7 +4900,7 @@ StackWalkAction SWCB_GetExecutionState(CrawlFrame *pCF, VOID *pData) // Note that the JIT always pushes LR even for leaf methods to make hijacking // work for them. See comment in code:Compiler::genPushCalleeSavedRegisters. -#if defined(TARGET_LOONGARCH64) +#if defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64) if (pRDT->pCallerContextPointers->Ra == &pRDT->pContext->Ra) #else if(pRDT->pCallerContextPointers->Lr == &pRDT->pContext->Lr) @@ -4939,7 +4939,7 @@ StackWalkAction SWCB_GetExecutionState(CrawlFrame *pCF, VOID *pData) // This is the case of IP being inside the method body and LR is // pushed on the stack. We get it to determine the return address // in the caller of the current non-interruptible frame. -#if defined(TARGET_LOONGARCH64) +#if defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64) pES->m_ppvRetAddrPtr = (void **) pRDT->pCallerContextPointers->Ra; #else pES->m_ppvRetAddrPtr = (void **) pRDT->pCallerContextPointers->Lr; From bc7f40491a908f8eb074119ec99857c436e53d14 Mon Sep 17 00:00:00 2001 From: Dong-Heon Jung Date: Thu, 23 Feb 2023 13:20:30 +0900 Subject: [PATCH 02/16] Remove commented codes --- src/coreclr/vm/fieldmarshaler.cpp | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/src/coreclr/vm/fieldmarshaler.cpp b/src/coreclr/vm/fieldmarshaler.cpp index 3be82af9fcd4cc..5ed44340450f1c 100644 --- a/src/coreclr/vm/fieldmarshaler.cpp +++ b/src/coreclr/vm/fieldmarshaler.cpp @@ -421,23 +421,6 @@ UINT32 NativeFieldDescriptor::AlignmentRequirement() const } } -#if 0 -PTR_MethodTable NativeFieldDescriptor::GetNestedNativeMethodTable() const -{ - CONTRACT(PTR_MethodTable) - { - NOTHROW; - GC_NOTRIGGER; - MODE_ANY; - PRECONDITION(IsNestedType()); - POSTCONDITION(CheckPointer(RETVAL)); - } - CONTRACT_END; - - RETURN nestedTypeAndCount.m_pNestedType; -} -#endif - PTR_FieldDesc NativeFieldDescriptor::GetFieldDesc() const { CONTRACT(PTR_FieldDesc) From 5513d20bbd3c9f05175fa945cb419433a3239647 Mon Sep 17 00:00:00 2001 From: Dong-Heon Jung Date: Thu, 23 Feb 2023 15:48:41 +0900 Subject: [PATCH 03/16] Fix mistakes --- src/coreclr/inc/regdisp.h | 1 - src/coreclr/inc/volatile.h | 4 ++-- src/coreclr/vm/jitinterface.cpp | 2 -- 3 files changed, 2 insertions(+), 5 deletions(-) diff --git a/src/coreclr/inc/regdisp.h b/src/coreclr/inc/regdisp.h index e7a230d2dab834..3f774f54a8dc71 100644 --- a/src/coreclr/inc/regdisp.h +++ b/src/coreclr/inc/regdisp.h @@ -426,7 +426,6 @@ inline void FillContextPointers(PT_KNONVOLATILE_CONTEXT_POINTERS pCtxPtrs, PT_CO *(&pCtxPtrs->Edi + i) = (&pCtx->Edi + i); } #elif defined(TARGET_RISCV64) // TARGET_X86 - // *(&pCtxPtrs->S0) = &pCtx->S0; *(&pCtxPtrs->S1) = &pCtx->S1; *(&pCtxPtrs->S2) = &pCtx->S2; *(&pCtxPtrs->S3) = &pCtx->S3; diff --git a/src/coreclr/inc/volatile.h b/src/coreclr/inc/volatile.h index 030638c2ef2a9a..177c4932166c69 100644 --- a/src/coreclr/inc/volatile.h +++ b/src/coreclr/inc/volatile.h @@ -68,8 +68,8 @@ #error The Volatile type is currently only defined for Visual C++ and GNU C++ #endif -#if defined(__GNUC__) && !defined(HOST_X86) && !defined(HOST_AMD64) && !defined(HOST_ARM) && !defined(HOST_ARM64) && !defined(HOST_LOONGARCH64) && !defined(HOST_RISCV64) && !defined(HOST_S390X) && !defined(HOST_POWERPC64) && !defined(HOST_RISCV64) -#error The Volatile type is currently only defined for GCC when targeting x86, AMD64, ARM, ARM64, LOONGARCH64, RISCV64, PPC64LE, S390X, or RISCV64 CPUs +#if defined(__GNUC__) && !defined(HOST_X86) && !defined(HOST_AMD64) && !defined(HOST_ARM) && !defined(HOST_ARM64) && !defined(HOST_LOONGARCH64) && !defined(HOST_RISCV64) && !defined(HOST_S390X) && !defined(HOST_POWERPC64) +#error The Volatile type is currently only defined for GCC when targeting x86, AMD64, ARM, ARM64, LOONGARCH64, RISCV64, PPC64LE, or S390X CPUs #endif #if defined(__GNUC__) diff --git a/src/coreclr/vm/jitinterface.cpp b/src/coreclr/vm/jitinterface.cpp index 767e136a21089f..9533a927e3f3ef 100644 --- a/src/coreclr/vm/jitinterface.cpp +++ b/src/coreclr/vm/jitinterface.cpp @@ -11260,8 +11260,6 @@ void CEEJitInfo::allocUnwindInfo ( ULONG * pPersonalityRoutineRW = (ULONG*)((BYTE *)pUnwindInfoRW + ALIGN_UP(unwindSize, sizeof(ULONG))); *pPersonalityRoutineRW = ExecutionManager::GetCLRPersonalityRoutineValue(); -; - #endif EE_TO_JIT_TRANSITION(); From fa4466a384a67cd8e4b318611812561d4d539b40 Mon Sep 17 00:00:00 2001 From: Dong-Heon Jung Date: Fri, 24 Feb 2023 15:36:28 +0900 Subject: [PATCH 04/16] Update by reviews --- src/coreclr/vm/codeman.cpp | 66 ++------------------------------------ 1 file changed, 2 insertions(+), 64 deletions(-) diff --git a/src/coreclr/vm/codeman.cpp b/src/coreclr/vm/codeman.cpp index 7080d2296a8d6a..ffcc7546b7d7f6 100644 --- a/src/coreclr/vm/codeman.cpp +++ b/src/coreclr/vm/codeman.cpp @@ -877,7 +877,7 @@ BOOL IsFunctionFragment(TADDR baseAddress, PTR_RUNTIME_FUNCTION pFunctionEntry) } return ((*pUnwindCodes & 0xFF) == 0xE5); -#elif defined(TARGET_LOONGARCH64) +#elif defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64) // LOONGARCH64 is a little bit more flexible, in the sense that it supports partial prologs. However only one of the // prolog regions are allowed to alter SP and that's the Host Record. Partial prologs are used in ShrinkWrapping @@ -911,29 +911,6 @@ BOOL IsFunctionFragment(TADDR baseAddress, PTR_RUNTIME_FUNCTION pFunctionEntry) pUnwindCodes += EpilogCount; } - return ((*pUnwindCodes & 0xFF) == 0xE5); -#elif defined(TARGET_RISCV64) - int EpilogCount = (int)(unwindHeader >> 22) & 0x1F; - int CodeWords = unwindHeader >> 27; - PTR_DWORD pUnwindCodes = (PTR_DWORD)(baseAddress + pFunctionEntry->UnwindData); - // Skip header. - pUnwindCodes++; - - // Skip extended header. - if ((CodeWords == 0) && (EpilogCount == 0)) - { - EpilogCount = (*pUnwindCodes) & 0xFFFF; - pUnwindCodes++; - } - - // Skip epilog scopes. - BOOL Ebit = (unwindHeader >> 21) & 0x1; - if (!Ebit && (EpilogCount != 0)) - { - // EpilogCount is the number of exception scopes defined right after the unwindHeader - pUnwindCodes += EpilogCount; - } - return ((*pUnwindCodes & 0xFF) == 0xE5); #else PORTABILITY_ASSERT("IsFunctionFragnent - NYI on this platform"); @@ -1099,46 +1076,7 @@ PTR_VOID GetUnwindDataBlob(TADDR moduleBase, PTR_RUNTIME_FUNCTION pRuntimeFuncti return xdata; -#elif defined(TARGET_LOONGARCH64) - // TODO: maybe optimize further. - // if this function uses packed unwind data then at least one of the two least significant bits - // will be non-zero. if this is the case then there will be no xdata record to enumerate. - _ASSERTE((pRuntimeFunction->UnwindData & 0x3) == 0); - - // compute the size of the unwind info - PTR_ULONG xdata = dac_cast(pRuntimeFunction->UnwindData + moduleBase); - ULONG epilogScopes = 0; - ULONG unwindWords = 0; - ULONG size = 0; - - //If both Epilog Count and Code Word is not zero - //Info of Epilog and Unwind scopes are given by 1 word header - //Otherwise this info is given by a 2 word header - if ((xdata[0] >> 27) != 0) - { - size = 4; - epilogScopes = (xdata[0] >> 22) & 0x1f; - unwindWords = (xdata[0] >> 27) & 0x1f; - } - else - { - size = 8; - epilogScopes = xdata[1] & 0xffff; - unwindWords = (xdata[1] >> 16) & 0xff; - } - - if (!(xdata[0] & (1 << 21))) - size += 4 * epilogScopes; - - size += 4 * unwindWords; - - _ASSERTE(xdata[0] & (1 << 20)); // personality routine should be always present - size += 4; // exception handler RVA - - *pSize = size; - return xdata; - -#elif defined(TARGET_RISCV64) +#elif defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64) // TODO: maybe optimize further. // if this function uses packed unwind data then at least one of the two least significant bits // will be non-zero. if this is the case then there will be no xdata record to enumerate. From ab2e6cd35f934cf2d63a62a700c8d2a364f31e4f Mon Sep 17 00:00:00 2001 From: Dong-Heon Jung Date: Tue, 7 Mar 2023 21:23:01 +0900 Subject: [PATCH 05/16] [VM] Fix test --- src/coreclr/vm/riscv64/cgencpu.h | 2 +- src/coreclr/vm/riscv64/stubs.cpp | 143 +++++++++++++++++++++++++++++-- 2 files changed, 138 insertions(+), 7 deletions(-) diff --git a/src/coreclr/vm/riscv64/cgencpu.h b/src/coreclr/vm/riscv64/cgencpu.h index 41e2cae04f65aa..d1bf8d66739fbf 100644 --- a/src/coreclr/vm/riscv64/cgencpu.h +++ b/src/coreclr/vm/riscv64/cgencpu.h @@ -91,8 +91,8 @@ inline unsigned StackElemSize(unsigned parmSize, bool isValueType, bool isFloatH //-------------------------------------------------------------------- typedef DPTR(struct CalleeSavedRegisters) PTR_CalleeSavedRegisters; struct CalleeSavedRegisters { - INT64 sp; // stack pointer INT64 fp; // frame pointer + INT64 ra; // return register INT64 s1, s2, s3, s4, s5, s6, s7, s8, s9, s10, s11; INT64 tp, gp; }; diff --git a/src/coreclr/vm/riscv64/stubs.cpp b/src/coreclr/vm/riscv64/stubs.cpp index 9d058bcad3197f..1df6b3d6349503 100644 --- a/src/coreclr/vm/riscv64/stubs.cpp +++ b/src/coreclr/vm/riscv64/stubs.cpp @@ -483,25 +483,156 @@ void ThisPtrRetBufPrecode::Init(MethodDesc* pMD, LoaderAllocator *pLoaderAllocat void UpdateRegDisplayFromCalleeSavedRegisters(REGDISPLAY * pRD, CalleeSavedRegisters * pCalleeSaved) { - _ASSERTE(!"RISCV64: not implementation on riscv64!!!"); + LIMITED_METHOD_CONTRACT; + pRD->pCurrentContext->S1 = pCalleeSaved->s1; + pRD->pCurrentContext->S2 = pCalleeSaved->s2; + pRD->pCurrentContext->S3 = pCalleeSaved->s3; + pRD->pCurrentContext->S4 = pCalleeSaved->s4; + pRD->pCurrentContext->S5 = pCalleeSaved->s5; + pRD->pCurrentContext->S6 = pCalleeSaved->s6; + pRD->pCurrentContext->S7 = pCalleeSaved->s7; + pRD->pCurrentContext->S8 = pCalleeSaved->s8; + pRD->pCurrentContext->S9 = pCalleeSaved->s9; + pRD->pCurrentContext->S10 = pCalleeSaved->s10; + pRD->pCurrentContext->S11 = pCalleeSaved->s11; + pRD->pCurrentContext->Gp = pCalleeSaved->gp; + pRD->pCurrentContext->Tp = pCalleeSaved->tp; + pRD->pCurrentContext->Fp = pCalleeSaved->fp; + pRD->pCurrentContext->Ra = pCalleeSaved->ra; + + T_KNONVOLATILE_CONTEXT_POINTERS * pContextPointers = pRD->pCurrentContextPointers; + pContextPointers->S1 = (PDWORD64)&pCalleeSaved->s1; + pContextPointers->S2 = (PDWORD64)&pCalleeSaved->s2; + pContextPointers->S3 = (PDWORD64)&pCalleeSaved->s3; + pContextPointers->S4 = (PDWORD64)&pCalleeSaved->s4; + pContextPointers->S5 = (PDWORD64)&pCalleeSaved->s5; + pContextPointers->S6 = (PDWORD64)&pCalleeSaved->s6; + pContextPointers->S7 = (PDWORD64)&pCalleeSaved->s7; + pContextPointers->S8 = (PDWORD64)&pCalleeSaved->s8; + pContextPointers->S9 = (PDWORD64)&pCalleeSaved->s9; + pContextPointers->S10 = (PDWORD64)&pCalleeSaved->s10; + pContextPointers->S11 = (PDWORD64)&pCalleeSaved->s11; + pContextPointers->Gp = (PDWORD64)&pCalleeSaved->gp; + pContextPointers->Tp = (PDWORD64)&pCalleeSaved->tp; + pContextPointers->Fp = (PDWORD64)&pCalleeSaved->fp; + pContextPointers->Ra = (PDWORD64)&pCalleeSaved->ra; } - void TransitionFrame::UpdateRegDisplay(const PREGDISPLAY pRD) { - _ASSERTE(!"RISCV64: not implementation on riscv64!!!"); -} + pRD->IsCallerContextValid = FALSE; + pRD->IsCallerSPValid = FALSE; // Don't add usage of this field. This is only temporary. + + // copy the callee saved regs + CalleeSavedRegisters *pCalleeSaved = GetCalleeSavedRegisters(); + UpdateRegDisplayFromCalleeSavedRegisters(pRD, pCalleeSaved); + + ClearRegDisplayArgumentAndScratchRegisters(pRD); + + // copy the control registers + //pRD->pCurrentContext->Fp = pCalleeSaved->fp;//not needed for duplicated. + //pRD->pCurrentContext->Ra = pCalleeSaved->ra;//not needed for duplicated. + pRD->pCurrentContext->Pc = GetReturnAddress(); + pRD->pCurrentContext->Sp = this->GetSP(); + // Finally, syncup the regdisplay with the context + SyncRegDisplayToCurrentContext(pRD); + LOG((LF_GCROOTS, LL_INFO100000, "STACKWALK TransitionFrame::UpdateRegDisplay(pc:%p, sp:%p)\n", pRD->ControlPC, pRD->SP)); +} void FaultingExceptionFrame::UpdateRegDisplay(const PREGDISPLAY pRD) { - _ASSERTE(!"RISCV64: not implementation on riscv64!!!"); + LIMITED_METHOD_DAC_CONTRACT; + + // Copy the context to regdisplay + memcpy(pRD->pCurrentContext, &m_ctx, sizeof(T_CONTEXT)); + + pRD->ControlPC = ::GetIP(&m_ctx); + pRD->SP = ::GetSP(&m_ctx); + + // Update the integer registers in KNONVOLATILE_CONTEXT_POINTERS from + // the exception context we have. + pRD->pCurrentContextPointers->S1 = (PDWORD64)&m_ctx.S1; + pRD->pCurrentContextPointers->S2 = (PDWORD64)&m_ctx.S2; + pRD->pCurrentContextPointers->S3 = (PDWORD64)&m_ctx.S3; + pRD->pCurrentContextPointers->S4 = (PDWORD64)&m_ctx.S4; + pRD->pCurrentContextPointers->S5 = (PDWORD64)&m_ctx.S5; + pRD->pCurrentContextPointers->S6 = (PDWORD64)&m_ctx.S6; + pRD->pCurrentContextPointers->S7 = (PDWORD64)&m_ctx.S7; + pRD->pCurrentContextPointers->S8 = (PDWORD64)&m_ctx.S8; + pRD->pCurrentContextPointers->S9 = (PDWORD64)&m_ctx.S9; + pRD->pCurrentContextPointers->S10 = (PDWORD64)&m_ctx.S10; + pRD->pCurrentContextPointers->S11 = (PDWORD64)&m_ctx.S11; + pRD->pCurrentContextPointers->Fp = (PDWORD64)&m_ctx.Fp; + pRD->pCurrentContextPointers->Gp = (PDWORD64)&m_ctx.Gp; + pRD->pCurrentContextPointers->Tp = (PDWORD64)&m_ctx.Tp; + pRD->pCurrentContextPointers->Ra = (PDWORD64)&m_ctx.Ra; + + ClearRegDisplayArgumentAndScratchRegisters(pRD); + + pRD->IsCallerContextValid = FALSE; + pRD->IsCallerSPValid = FALSE; // Don't add usage of this field. This is only temporary. + + LOG((LF_GCROOTS, LL_INFO100000, "STACKWALK FaultingExceptionFrame::UpdateRegDisplay(pc:%p, sp:%p)\n", pRD->ControlPC, pRD->SP)); } void InlinedCallFrame::UpdateRegDisplay(const PREGDISPLAY pRD) { - _ASSERTE(!"RISCV64: not implementation on riscv64!!!"); + CONTRACT_VOID + { + NOTHROW; + GC_NOTRIGGER; +#ifdef PROFILING_SUPPORTED + PRECONDITION(CORProfilerStackSnapshotEnabled() || InlinedCallFrame::FrameHasActiveCall(this)); +#endif + HOST_NOCALLS; + MODE_ANY; + SUPPORTS_DAC; + } + CONTRACT_END; + + if (!InlinedCallFrame::FrameHasActiveCall(this)) + { + LOG((LF_CORDB, LL_ERROR, "WARNING: InlinedCallFrame::UpdateRegDisplay called on inactive frame %p\n", this)); + return; + } + + pRD->IsCallerContextValid = FALSE; + pRD->IsCallerSPValid = FALSE; + + pRD->pCurrentContext->Pc = *(DWORD64 *)&m_pCallerReturnAddress; + pRD->pCurrentContext->Sp = *(DWORD64 *)&m_pCallSiteSP; + pRD->pCurrentContext->Fp = *(DWORD64 *)&m_pCalleeSavedFP; + + pRD->pCurrentContextPointers->S1 = NULL; + pRD->pCurrentContextPointers->S2 = NULL; + pRD->pCurrentContextPointers->S3 = NULL; + pRD->pCurrentContextPointers->S4 = NULL; + pRD->pCurrentContextPointers->S5 = NULL; + pRD->pCurrentContextPointers->S6 = NULL; + pRD->pCurrentContextPointers->S7 = NULL; + pRD->pCurrentContextPointers->S8 = NULL; + pRD->pCurrentContextPointers->S9 = NULL; + pRD->pCurrentContextPointers->S10 = NULL; + pRD->pCurrentContextPointers->S11 = NULL; + pRD->pCurrentContextPointers->Gp = NULL; + pRD->pCurrentContextPointers->Tp = NULL; + + pRD->ControlPC = m_pCallerReturnAddress; + pRD->SP = (DWORD64) dac_cast(m_pCallSiteSP); + + // reset pContext; it's only valid for active (top-most) frame + pRD->pContext = NULL; + + ClearRegDisplayArgumentAndScratchRegisters(pRD); + + + // Update the frame pointer in the current context. + pRD->pCurrentContextPointers->Fp = &m_pCalleeSavedFP; + + LOG((LF_GCROOTS, LL_INFO100000, "STACKWALK InlinedCallFrame::UpdateRegDisplay(pc:%p, sp:%p)\n", pRD->ControlPC, pRD->SP)); + RETURN; } From 39b96f8912854f0c95ea4cc0181f9aa5e3152251 Mon Sep 17 00:00:00 2001 From: Dong-Heon Jung Date: Wed, 8 Mar 2023 11:31:02 +0900 Subject: [PATCH 06/16] [VM] Update Updated by review in #82380 --- src/coreclr/vm/riscv64/asmhelpers.S | 12 ++++++++++++ src/coreclr/vm/riscv64/pinvokestubs.S | 2 ++ 2 files changed, 14 insertions(+) diff --git a/src/coreclr/vm/riscv64/asmhelpers.S b/src/coreclr/vm/riscv64/asmhelpers.S index cc063af47a2ae6..9b4715964df651 100644 --- a/src/coreclr/vm/riscv64/asmhelpers.S +++ b/src/coreclr/vm/riscv64/asmhelpers.S @@ -366,6 +366,7 @@ LOCAL_LABEL(NoRestore_\reg): NESTED_ENTRY ThePreStub, _TEXT, NoHandler PROLOG_WITH_TRANSITION_BLOCK + .cfi_adjust_cfa_offset __PWTB_StackAlloc addi a1, METHODDESC_REGISTER, 0 // pMethodDesc @@ -374,6 +375,7 @@ NESTED_ENTRY ThePreStub, _TEXT, NoHandler addi t4, a0, 0 EPILOG_WITH_TRANSITION_BLOCK_TAILCALL + .cfi_adjust_cfa_offset -__PWTB_StackAlloc EPILOG_BRANCH_REG t4 NESTED_END ThePreStub, _TEXT @@ -608,6 +610,7 @@ NESTED_END ResolveWorkerChainLookupAsmStub, _TEXT // The stub dispatch thunk which transfers control to VSD_ResolveWorker. NESTED_ENTRY ResolveWorkerAsmStub, _TEXT, NoHandler PROLOG_WITH_TRANSITION_BLOCK + .cfi_adjust_cfa_offset __PWTB_StackAlloc addi a2, t2, 0 // DispatchToken addi a0, sp, __PWTB_TransitionBlock // pTransitionBlock @@ -618,6 +621,7 @@ NESTED_ENTRY ResolveWorkerAsmStub, _TEXT, NoHandler addi t4, a0, 0 EPILOG_WITH_TRANSITION_BLOCK_TAILCALL + .cfi_adjust_cfa_offset -__PWTB_StackAlloc EPILOG_BRANCH_REG t4 NESTED_END ResolveWorkerAsmStub, _TEXT @@ -831,6 +835,7 @@ NESTED_ENTRY DelayLoad_MethodCall_FakeProlog, _TEXT, NoHandler C_FUNC(DelayLoad_MethodCall): .global C_FUNC(DelayLoad_MethodCall) PROLOG_WITH_TRANSITION_BLOCK + .cfi_adjust_cfa_offset __PWTB_StackAlloc addi a1, t5, 0 // Indirection cell addi a2, t0, 0 // sectionIndex @@ -841,6 +846,7 @@ C_FUNC(DelayLoad_MethodCall): addi t4, a0, 0 EPILOG_WITH_TRANSITION_BLOCK_TAILCALL + .cfi_adjust_cfa_offset -__PWTB_StackAlloc PATCH_LABEL ExternalMethodFixupPatchLabel EPILOG_BRANCH_REG t4 NESTED_END DelayLoad_MethodCall_FakeProlog, _TEXT @@ -852,6 +858,7 @@ DelayLoad_Helper\suffix: .global DelayLoad_Helper\suffix PROLOG_WITH_TRANSITION_BLOCK + .cfi_adjust_cfa_offset __PWTB_StackAlloc //DynamicHelperWorker(TransitionBlock * pTransitionBlock, TADDR * pCell, // DWORD sectionIndex, Module * pModule, INT frameFlags) @@ -867,9 +874,12 @@ DelayLoad_Helper\suffix: ld a0, __PWTB_ArgumentRegisters(sp) EPILOG_WITH_TRANSITION_BLOCK_RETURN + .cfi_adjust_cfa_offset -__PWTB_StackAlloc + LOCAL_LABEL(FakeProlog\suffix\()_0): addi t4, a0, 0 EPILOG_WITH_TRANSITION_BLOCK_TAILCALL + .cfi_adjust_cfa_offset -__PWTB_StackAlloc EPILOG_BRANCH_REG t4 NESTED_END DelayLoad_Helper\suffix\()_FakeProlog, _TEXT @@ -942,6 +952,7 @@ GenerateProfileHelper ProfileTailcall, PROFILE_TAILCALL NESTED_ENTRY OnCallCountThresholdReachedStub, _TEXT, NoHandler PROLOG_WITH_TRANSITION_BLOCK + .cfi_adjust_cfa_offset __PWTB_StackAlloc addi a0, sp, __PWTB_TransitionBlock // TransitionBlock * addi a1, t3, 0 // stub-identifying token @@ -949,6 +960,7 @@ NESTED_ENTRY OnCallCountThresholdReachedStub, _TEXT, NoHandler addi t4, a0, 0 EPILOG_WITH_TRANSITION_BLOCK_TAILCALL + .cfi_adjust_cfa_offset -__PWTB_StackAlloc EPILOG_BRANCH_REG t4 NESTED_END OnCallCountThresholdReachedStub, _TEXT diff --git a/src/coreclr/vm/riscv64/pinvokestubs.S b/src/coreclr/vm/riscv64/pinvokestubs.S index ea5d245c56a0d6..5df5af6af71f61 100644 --- a/src/coreclr/vm/riscv64/pinvokestubs.S +++ b/src/coreclr/vm/riscv64/pinvokestubs.S @@ -42,6 +42,7 @@ NESTED_ENTRY \__PInvokeGenStubFuncName, _TEXT, NoHandler PROLOG_WITH_TRANSITION_BLOCK 0, 0, \SaveFPArgs + .cfi_adjust_cfa_offset __PWTB_StackAlloc // a2 = Umanaged Target\MethodDesc addi a2, \HiddenArg, 0 @@ -67,6 +68,7 @@ addi \HiddenArg, s1, 0 EPILOG_WITH_TRANSITION_BLOCK_TAILCALL + .cfi_adjust_cfa_offset -__PWTB_StackAlloc EPILOG_BRANCH C_FUNC(\__PInvokeStubFuncName) NESTED_END \__PInvokeGenStubFuncName, _TEXT From cc6fda67277011fb133773d5ef9dc137563a194b Mon Sep 17 00:00:00 2001 From: Dong-Heon Jung Date: Wed, 8 Mar 2023 15:12:44 +0900 Subject: [PATCH 07/16] [VM] Update assert and todo comments --- src/coreclr/gc/env/gcenv.base.h | 4 ++-- src/coreclr/gcinfo/gcinfodumper.cpp | 2 +- src/coreclr/inc/clrnt.h | 1 - src/coreclr/inc/gcinfotypes.h | 2 +- src/coreclr/vm/callcounting.h | 2 +- src/coreclr/vm/gccover.cpp | 1 - src/coreclr/vm/gccover.h | 4 ++-- src/coreclr/vm/riscv64/cgencpu.h | 7 ++---- src/coreclr/vm/riscv64/profiler.cpp | 24 ++++++++++++------- src/coreclr/vm/riscv64/virtualcallstubcpu.hpp | 6 ++--- 10 files changed, 28 insertions(+), 25 deletions(-) diff --git a/src/coreclr/gc/env/gcenv.base.h b/src/coreclr/gc/env/gcenv.base.h index d6f6e6161b2543..2f7ff958ce7d6b 100644 --- a/src/coreclr/gc/env/gcenv.base.h +++ b/src/coreclr/gc/env/gcenv.base.h @@ -226,8 +226,8 @@ typedef DWORD (WINAPI *PTHREAD_START_ROUTINE)(void* lpThreadParameter); #define MemoryBarrier __sync_synchronize #endif // __loongarch64 -#ifdef __riscv // TODO RISCV64 - #define YieldProcessor() asm volatile( "fence iorw, iorw"); // TODO +#ifdef __riscv + #define YieldProcessor() asm volatile( "fence iorw, iorw"); #define MemoryBarrier __sync_synchronize #endif // __riscv diff --git a/src/coreclr/gcinfo/gcinfodumper.cpp b/src/coreclr/gcinfo/gcinfodumper.cpp index be45bc3feb2ee2..d412fb618fe063 100644 --- a/src/coreclr/gcinfo/gcinfodumper.cpp +++ b/src/coreclr/gcinfo/gcinfodumper.cpp @@ -704,7 +704,7 @@ GcInfoDumper::EnumerateStateChangesResults GcInfoDumper::EnumerateStateChanges ( assert(!"unimplemented on LOONGARCH yet"); #elif defined(TARGET_RISCV64) #pragma message("Unimplemented for RISCV64 yet.") - assert(!"unimplemented on RISCV64 yet"); // TODO RISCV64 + assert(!"unimplemented on RISCV64 yet"); #else PORTABILITY_ASSERT("GcInfoDumper::EnumerateStateChanges is not implemented on this platform."); #endif diff --git a/src/coreclr/inc/clrnt.h b/src/coreclr/inc/clrnt.h index 3da9a6abf56787..8aa6b5498289da 100644 --- a/src/coreclr/inc/clrnt.h +++ b/src/coreclr/inc/clrnt.h @@ -1100,7 +1100,6 @@ RtlpGetFunctionEndAddress ( _In_ ULONG64 ImageBase ) { - // TODO RISCV64 ULONG64 FunctionLength; FunctionLength = FunctionEntry->UnwindData; diff --git a/src/coreclr/inc/gcinfotypes.h b/src/coreclr/inc/gcinfotypes.h index 33759d14d949ef..f9fa62eb8cbb6c 100644 --- a/src/coreclr/inc/gcinfotypes.h +++ b/src/coreclr/inc/gcinfotypes.h @@ -860,7 +860,7 @@ void FASTCALL decodeCallPattern(int pattern, #define CODE_LENGTH_ENCBASE 8 #define SIZE_OF_RETURN_KIND_IN_SLIM_HEADER 2 #define SIZE_OF_RETURN_KIND_IN_FAT_HEADER 4 -#define STACK_BASE_REGISTER_ENCBASE 2 // TODO for RISCV64 +#define STACK_BASE_REGISTER_ENCBASE 2 // FP encoded as 0, SP as 2?? #define SIZE_OF_STACK_AREA_ENCBASE 3 #define SIZE_OF_EDIT_AND_CONTINUE_PRESERVED_AREA_ENCBASE 4 diff --git a/src/coreclr/vm/callcounting.h b/src/coreclr/vm/callcounting.h index f47bfc05c16757..0e153c47d86d3f 100644 --- a/src/coreclr/vm/callcounting.h +++ b/src/coreclr/vm/callcounting.h @@ -100,7 +100,7 @@ class CallCountingStub #elif defined(TARGET_LOONGARCH64) static const int CodeSize = 40; #elif defined(TARGET_RISCV64) - static const int CodeSize = 40; // TODO RISCV64 + static const int CodeSize = 40; #endif private: diff --git a/src/coreclr/vm/gccover.cpp b/src/coreclr/vm/gccover.cpp index 0b2592ad763a44..db95c3b6971aeb 100644 --- a/src/coreclr/vm/gccover.cpp +++ b/src/coreclr/vm/gccover.cpp @@ -1670,7 +1670,6 @@ void DoGcStress (PCONTEXT regs, NativeCodeVersion nativeCodeVersion) #elif defined(TARGET_LOONGARCH64) *(DWORD*)nextInstrWriterHolder.GetRW() = INTERRUPT_INSTR; #elif defined(TARGET_RISCV64) - _ASSERTE(!"TODO RISCV64 NYI"); *(DWORD*)nextInstrWriterHolder.GetRW() = INTERRUPT_INSTR; #else *nextInstrWriterHolder.GetRW() = INTERRUPT_INSTR; diff --git a/src/coreclr/vm/gccover.h b/src/coreclr/vm/gccover.h index 3f0a04bcac5a9e..9e61f1f7b623cc 100644 --- a/src/coreclr/vm/gccover.h +++ b/src/coreclr/vm/gccover.h @@ -113,7 +113,7 @@ typedef DPTR(GCCoverageInfo) PTR_GCCoverageInfo; // see code:GCCoverageInfo::sav #define INTERRUPT_INSTR_PROTECT_RET 0xffffff0d #elif defined(TARGET_RISCV64) -// TODO RISCV64 NYI +// TODO-RISCV64: Confirm the following encodings are undefined #define INTERRUPT_INSTR 0xBADC0DE0 #define INTERRUPT_INSTR_CALL 0xBADC0DE1 #define INTERRUPT_INSTR_PROTECT_RET 0xBADC0DE2 @@ -181,7 +181,7 @@ inline bool IsGcCoverageInterruptInstructionVal(UINT32 instrVal) } } #elif defined(TARGET_RISCV64) - _ASSERTE(!"TODO RISCV64 NYI"); + _ASSERTE(!"RISCV64:NYI"); return false; #else // x64 and x86 diff --git a/src/coreclr/vm/riscv64/cgencpu.h b/src/coreclr/vm/riscv64/cgencpu.h index d1bf8d66739fbf..3270d2aaa5dffc 100644 --- a/src/coreclr/vm/riscv64/cgencpu.h +++ b/src/coreclr/vm/riscv64/cgencpu.h @@ -53,7 +53,6 @@ extern PCODE GetPreStubEntryPoint(); #define CALLDESCR_ARGREGS 1 // CallDescrWorker has ArgumentRegister parameter #define CALLDESCR_FPARGREGS 1 // CallDescrWorker has FloatArgumentRegisters parameter -// #define CALLDESCR_RETBUFFARGREG 1 // CallDescrWorker has RetBuffArg parameter that's separate from arg regs // TODO RISCV64 #define FLOAT_REGISTER_SIZE 16 // each register in FloatArgumentRegisters is 16 bytes. @@ -346,7 +345,6 @@ const IntReg RegRa = IntReg(1); #define GetEEFuncEntryPoint(pfn) GFN_TADDR(pfn) -#if 1 // TODO RISCV64 class StubLinkerCPU : public StubLinker { @@ -373,9 +371,9 @@ class StubLinkerCPU : public StubLinker #endif // FEATURE_SHARE_GENERIC_CODE #ifdef _DEBUG - void EmitNop() { _ASSERTE(!"RISCV64: not implementation on riscv64!!!"); } + void EmitNop() { _ASSERTE(!"RISCV64:NYI "); } #endif - void EmitBreakPoint() { _ASSERTE(!"RISCV64: not implementation on riscv64!!!"); } + void EmitBreakPoint() { _ASSERTE(!"RISCV64:NYI"); } void EmitMovConstant(IntReg target, UINT64 constant); void EmitCmpImm(IntReg reg, int imm); void EmitCmpReg(IntReg Xn, IntReg Xm); @@ -397,7 +395,6 @@ class StubLinkerCPU : public StubLinker void EmitCallRegister(IntReg reg); void EmitRet(IntReg reg); }; -#endif extern "C" void SinglecastDelegateInvokeStub(); diff --git a/src/coreclr/vm/riscv64/profiler.cpp b/src/coreclr/vm/riscv64/profiler.cpp index fc0e224b65a427..5b9feb0e4ebdd0 100644 --- a/src/coreclr/vm/riscv64/profiler.cpp +++ b/src/coreclr/vm/riscv64/profiler.cpp @@ -31,7 +31,8 @@ typedef struct _PROFILE_PLATFORM_SPECIFIC_DATA UINT_PTR ProfileGetIPFromPlatformSpecificHandle(void* pPlatformSpecificHandle) { - _ASSERTE(!"TODO RISCV64 NYI"); + // TODO-RISCV64-CQ: copied codes from loongarch however not yet tested. + _ASSERTE(!"RISCV64:NYI"); LIMITED_METHOD_CONTRACT; PROFILE_PLATFORM_SPECIFIC_DATA* pData = reinterpret_cast(pPlatformSpecificHandle); @@ -40,7 +41,8 @@ UINT_PTR ProfileGetIPFromPlatformSpecificHandle(void* pPlatformSpecificHandle) void ProfileSetFunctionIDInPlatformSpecificHandle(void* pPlatformSpecificHandle, FunctionID functionId) { - _ASSERTE(!"TODO RISCV64 NYI"); + // TODO-RISCV64-CQ: copied codes from loongarch however not yet tested. + _ASSERTE(!"RISCV64:NYI"); LIMITED_METHOD_CONTRACT; _ASSERTE(pPlatformSpecificHandle != nullptr); @@ -53,7 +55,8 @@ void ProfileSetFunctionIDInPlatformSpecificHandle(void* pPlatformSpecificHandle, ProfileArgIterator::ProfileArgIterator(MetaSig* pSig, void* pPlatformSpecificHandle) : m_argIterator(pSig) { - _ASSERTE(!"TODO RISCV64 NYI"); + // TODO-RISCV64-CQ: copied codes from loongarch however not yet tested. + _ASSERTE(!"RISCV64:NYI"); WRAPPER_NO_CONTRACT; _ASSERTE(pSig != nullptr); @@ -116,7 +119,8 @@ ProfileArgIterator::ProfileArgIterator(MetaSig* pSig, void* pPlatformSpecificHan ProfileArgIterator::~ProfileArgIterator() { - _ASSERTE(!"TODO RISCV64 NYI"); + // TODO-RISCV64-CQ: copied codes from loongarch however not yet tested. + _ASSERTE(!"RISCV64:NYI"); LIMITED_METHOD_CONTRACT; m_handle = nullptr; @@ -124,7 +128,8 @@ ProfileArgIterator::~ProfileArgIterator() LPVOID ProfileArgIterator::GetNextArgAddr() { - _ASSERTE(!"TODO RISCV64 NYI"); + // TODO-RISCV64-CQ: copied codes from loongarch however not yet tested. + _ASSERTE(!"RISCV64:NYI"); WRAPPER_NO_CONTRACT; _ASSERTE(m_handle != nullptr); @@ -172,7 +177,8 @@ LPVOID ProfileArgIterator::GetNextArgAddr() LPVOID ProfileArgIterator::GetHiddenArgValue(void) { - _ASSERTE(!"TODO RISCV64 NYI"); + // TODO-RISCV64-CQ: copied codes from loongarch however not yet tested. + _ASSERTE(!"RISCV64:NYI"); LIMITED_METHOD_CONTRACT; PROFILE_PLATFORM_SPECIFIC_DATA* pData = reinterpret_cast(m_handle); @@ -182,7 +188,8 @@ LPVOID ProfileArgIterator::GetHiddenArgValue(void) LPVOID ProfileArgIterator::GetThis(void) { - _ASSERTE(!"TODO RISCV64 NYI"); + // TODO-RISCV64-CQ: copied codes from loongarch however not yet tested. + _ASSERTE(!"RISCV64:NYI"); CONTRACTL { NOTHROW; @@ -216,7 +223,8 @@ LPVOID ProfileArgIterator::GetThis(void) LPVOID ProfileArgIterator::GetReturnBufferAddr(void) { - _ASSERTE(!"TODO RISCV64 NYI"); + // TODO-RISCV64-CQ: copied codes from loongarch however not yet tested. + _ASSERTE(!"RISCV64:NYI"); CONTRACTL { NOTHROW; diff --git a/src/coreclr/vm/riscv64/virtualcallstubcpu.hpp b/src/coreclr/vm/riscv64/virtualcallstubcpu.hpp index 688c6eac727ec9..c036d17c01408f 100644 --- a/src/coreclr/vm/riscv64/virtualcallstubcpu.hpp +++ b/src/coreclr/vm/riscv64/virtualcallstubcpu.hpp @@ -159,7 +159,7 @@ struct ResolveStub private: friend struct ResolveHolder; - const static int resolveEntryPointLen = 20; // TODO RISCV64 + const static int resolveEntryPointLen = 20; const static int slowEntryPointLen = 4; const static int failEntryPointLen = 9; @@ -187,12 +187,12 @@ struct ResolveHolder /******** Rough Convention of used in this routine ;;ra temp base address of loading data region - ;;t5 indirection cell // TODO CHECK t8 => t5 + ;;t5 indirection cell ;;t3 MethodTable (from object ref in a0), out: this._token ;;t0 hash scratch ;;t1 temp ;;t2 temp - ;;t6 hash scratch // TODO CHECK R21 => t6 + ;;t6 hash scratch ;;cachemask => [CALL_STUB_CACHE_MASK * sizeof(void*)] // Called directly by JITTED code From c5e01f47d513e4d7fb547312a12bc8258e09e0c2 Mon Sep 17 00:00:00 2001 From: Dong-Heon Jung Date: Wed, 22 Mar 2023 16:30:19 +0900 Subject: [PATCH 08/16] [VM] Updated --- src/coreclr/gc/env/gcenv.base.h | 2 +- src/coreclr/gcinfo/CMakeLists.txt | 6 +-- src/coreclr/inc/clrconfigvalues.h | 2 + src/coreclr/inc/cordebuginfo.h | 2 +- src/coreclr/vm/arm64/cgencpu.h | 1 + src/coreclr/vm/gccover.h | 6 +-- src/coreclr/vm/riscv64/asmhelpers.S | 38 +++++++++---------- .../vm/riscv64/calldescrworkerriscv64.S | 32 ++++++++-------- src/coreclr/vm/riscv64/cgencpu.h | 2 +- src/coreclr/vm/riscv64/crthelpers.S | 4 +- src/coreclr/vm/riscv64/pinvokestubs.S | 4 +- src/coreclr/vm/riscv64/stubs.cpp | 14 +++---- src/coreclr/vm/riscv64/thunktemplates.S | 8 ++-- 13 files changed, 62 insertions(+), 59 deletions(-) diff --git a/src/coreclr/gc/env/gcenv.base.h b/src/coreclr/gc/env/gcenv.base.h index 2f7ff958ce7d6b..816219ecaf2947 100644 --- a/src/coreclr/gc/env/gcenv.base.h +++ b/src/coreclr/gc/env/gcenv.base.h @@ -227,7 +227,7 @@ typedef DWORD (WINAPI *PTHREAD_START_ROUTINE)(void* lpThreadParameter); #endif // __loongarch64 #ifdef __riscv - #define YieldProcessor() asm volatile( "fence iorw, iorw"); + #define YieldProcessor() asm volatile( ".word 0x0100000f"); #define MemoryBarrier __sync_synchronize #endif // __riscv diff --git a/src/coreclr/gcinfo/CMakeLists.txt b/src/coreclr/gcinfo/CMakeLists.txt index f1dcd7fc89d3a8..c66a334be7dee4 100644 --- a/src/coreclr/gcinfo/CMakeLists.txt +++ b/src/coreclr/gcinfo/CMakeLists.txt @@ -81,11 +81,11 @@ endif (CLR_CMAKE_TARGET_ARCH_LOONGARCH64) if (CLR_CMAKE_TARGET_ARCH_RISCV64) create_gcinfo_lib(TARGET gcinfo_unix_riscv64 OS unix ARCH riscv64) -else() - create_gcinfo_lib(TARGET gcinfo_universal_arm OS universal ARCH arm) - create_gcinfo_lib(TARGET gcinfo_win_x86 OS win ARCH x86) endif (CLR_CMAKE_TARGET_ARCH_RISCV64) +create_gcinfo_lib(TARGET gcinfo_universal_arm OS universal ARCH arm) +create_gcinfo_lib(TARGET gcinfo_win_x86 OS win ARCH x86) + if (CLR_CMAKE_TARGET_ARCH_I386 AND CLR_CMAKE_TARGET_UNIX) create_gcinfo_lib(TARGET gcinfo_unix_x86 OS unix ARCH x86) endif (CLR_CMAKE_TARGET_ARCH_I386 AND CLR_CMAKE_TARGET_UNIX) diff --git a/src/coreclr/inc/clrconfigvalues.h b/src/coreclr/inc/clrconfigvalues.h index e1c0669210b69c..a99cd3227b8778 100644 --- a/src/coreclr/inc/clrconfigvalues.h +++ b/src/coreclr/inc/clrconfigvalues.h @@ -295,6 +295,7 @@ CONFIG_DWORD_INFO(INTERNAL_JitDebuggable, W("JitDebuggable"), 0, "") RETAIL_CONFIG_DWORD_INFO(INTERNAL_JitEnableNoWayAssert, W("JitEnableNoWayAssert"), INTERNAL_JitEnableNoWayAssert_Default, "") #if defined(TARGET_RISCV64) +// TODO-RISCV64-CQ: In RISCV64, currently jitc always generates JitFramed codes. RETAIL_CONFIG_DWORD_INFO(UNSUPPORTED_JitFramed, W("JitFramed"), 1, "Forces EBP frames") #else RETAIL_CONFIG_DWORD_INFO(UNSUPPORTED_JitFramed, W("JitFramed"), 0, "Forces EBP frames") @@ -744,6 +745,7 @@ RETAIL_CONFIG_DWORD_INFO(INTERNAL_GDBJitEmitDebugFrame, W("GDBJitEmitDebugFrame" // #if defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64) //TODO: should implement LoongArch64's features. +//TODO-RISCV64-CQ: should implement RISCV64's features. RETAIL_CONFIG_DWORD_INFO(EXTERNAL_EnableHWIntrinsic, W("EnableHWIntrinsic"), 0, "Allows Base+ hardware intrinsics to be disabled") #else RETAIL_CONFIG_DWORD_INFO(EXTERNAL_EnableHWIntrinsic, W("EnableHWIntrinsic"), 1, "Allows Base+ hardware intrinsics to be disabled") diff --git a/src/coreclr/inc/cordebuginfo.h b/src/coreclr/inc/cordebuginfo.h index 01780570570e48..e894a9beb69f9b 100644 --- a/src/coreclr/inc/cordebuginfo.h +++ b/src/coreclr/inc/cordebuginfo.h @@ -188,7 +188,7 @@ class ICorDebugInfo REGNUM_T0, REGNUM_T1, REGNUM_T2, - REGNUM_S0, + REGNUM_FP, REGNUM_S1, REGNUM_A0, REGNUM_A1, diff --git a/src/coreclr/vm/arm64/cgencpu.h b/src/coreclr/vm/arm64/cgencpu.h index 39c3184aaea07f..ea29ec2bdce028 100644 --- a/src/coreclr/vm/arm64/cgencpu.h +++ b/src/coreclr/vm/arm64/cgencpu.h @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. // + #ifndef TARGET_ARM64 #error Should only include "cGenCpu.h" for ARM64 builds #endif diff --git a/src/coreclr/vm/gccover.h b/src/coreclr/vm/gccover.h index 9e61f1f7b623cc..3ce8ad1e7cd911 100644 --- a/src/coreclr/vm/gccover.h +++ b/src/coreclr/vm/gccover.h @@ -114,9 +114,9 @@ typedef DPTR(GCCoverageInfo) PTR_GCCoverageInfo; // see code:GCCoverageInfo::sav #elif defined(TARGET_RISCV64) // TODO-RISCV64: Confirm the following encodings are undefined -#define INTERRUPT_INSTR 0xBADC0DE0 -#define INTERRUPT_INSTR_CALL 0xBADC0DE1 -#define INTERRUPT_INSTR_PROTECT_RET 0xBADC0DE2 +#define INTERRUPT_INSTR 0x20000000 +#define INTERRUPT_INSTR_CALL 0x20010000 +#define INTERRUPT_INSTR_PROTECT_RET 0x20020000 #endif // _TARGET_* diff --git a/src/coreclr/vm/riscv64/asmhelpers.S b/src/coreclr/vm/riscv64/asmhelpers.S index 9b4715964df651..852d477f788591 100644 --- a/src/coreclr/vm/riscv64/asmhelpers.S +++ b/src/coreclr/vm/riscv64/asmhelpers.S @@ -6,13 +6,13 @@ LEAF_ENTRY GetCurrentIP, _TEXT addi a0, ra, 0 - jalr x0, ra, 0 + ret LEAF_END GetCurrentIP, _TEXT // LPVOID __stdcall GetCurrentSP(void)// LEAF_ENTRY GetCurrentSP, _TEXT addi a0, sp, 0 - jalr x0, ra, 0 + ret LEAF_END GetCurrentSP, _TEXT //----------------------------------------------------------------------------- @@ -117,7 +117,7 @@ LEAF_ENTRY JIT_WriteBarrier_Callable, _TEXT // Branch to the write barrier lla t1, JIT_WriteBarrier_Loc ld t1, 0(t1) - jalr x0, t1, 0 + jr t1 LEAF_END JIT_WriteBarrier_Callable, _TEXT @@ -125,7 +125,7 @@ LEAF_END JIT_WriteBarrier_Callable, _TEXT // ------------------------------------------------------------------ // Start of the writeable code region LEAF_ENTRY JIT_PatchedCodeStart, _TEXT - jalr x0, ra, 0 + ret LEAF_END JIT_PatchedCodeStart, _TEXT // void JIT_ByRefWriteBarrier @@ -174,7 +174,7 @@ WRITE_BARRIER_ENTRY JIT_CheckedWriteBarrier sd t4, 0(t3) addi t3, t3, 8 - jalr x0, ra, 0 + ret WRITE_BARRIER_END JIT_CheckedWriteBarrier // void JIT_WriteBarrier(Object** dst, Object* src) @@ -294,7 +294,7 @@ LOCAL_LABEL(SkipEphemeralCheck): #endif LOCAL_LABEL(Exit): addi t3, t3, 8 - jalr x0, ra, 0 + ret WRITE_BARRIER_END JIT_WriteBarrier // Begin patchable literal pool @@ -322,7 +322,7 @@ WRITE_BARRIER_END JIT_WriteBarrier_Table // ------------------------------------------------------------------ // End of the writeable code region LEAF_ENTRY JIT_PatchedCodeLast, _TEXT - jalr x0, ra, 0 + ret LEAF_END JIT_PatchedCodeLast, _TEXT @@ -415,7 +415,7 @@ LOCAL_LABEL(Done): // as it is used in the state machine to loop until it becomes zero. // Refer to HELPER_METHOD_FRAME_END macro for details. addi a0, zero, 0 - jalr x0, ra, 0 + ret LEAF_END HelperMethodFrameRestoreState, _TEXT //----------------------------------------------------------------------------- @@ -449,7 +449,7 @@ LEAF_ENTRY LazyMachStateCaptureState, _TEXT sd tp, 96(a1) sd gp, 104(a1) - jalr x0, ra, 0 + ret LEAF_END LazyMachStateCaptureState, _TEXT // ------------------------------------------------------------------ @@ -480,7 +480,7 @@ LEAF_ENTRY SinglecastDelegateInvokeStub, _TEXT ld t4, (DelegateObject___methodPtr)(a0) ld a0, (DelegateObject___target)(a0) - jalr x0, t4, 0 + jr t4 LOCAL_LABEL(LNullThis): addi a0, zero, CORINFO_NullReferenceException_ASM @@ -492,7 +492,7 @@ LEAF_END SinglecastDelegateInvokeStub, _TEXT LEAF_ENTRY ThePreStubPatch, _TEXT .globl C_FUNC(ThePreStubPatchLabel) C_FUNC(ThePreStubPatchLabel): - jalr x0, ra, 0 + ret LEAF_END ThePreStubPatch, _TEXT NESTED_ENTRY TheUMEntryPrestub, _TEXT, UnhandledExceptionHandlerUnix @@ -529,7 +529,7 @@ LEAF_ENTRY JIT_GetSharedGCStaticBase_SingleAppDomain, _TEXT beq t5, zero, LOCAL_LABEL(JIT_GetSharedGCStaticBase_SingleAppDomain_CallHelper) ld a0, DomainLocalModule__m_pGCStatics(a0) - jalr x0, ra, 0 + ret LOCAL_LABEL(JIT_GetSharedGCStaticBase_SingleAppDomain_CallHelper): // Tail call JIT_GetSharedGCStaticBase_Helper @@ -591,7 +591,7 @@ LOCAL_LABEL(Success): blt t4, zero, LOCAL_LABEL(Promote) ld t4, (ResolveCacheElem__target)(t1) // get the ImplTarget - jalr x0, t4, 0 // branch to interface implementation target + jr t4 // branch to interface implementation target LOCAL_LABEL(Promote): // Move this entry to head position of the chain @@ -630,7 +630,7 @@ NESTED_END ResolveWorkerAsmStub, _TEXT // void* JIT_GetSharedNonGCStaticBaseNoCtor(SIZE_T moduleDomainID, DWORD dwClassDomainID) LEAF_ENTRY JIT_GetSharedNonGCStaticBaseNoCtor_SingleAppDomain, _TEXT - jalr x0, ra, 0 + ret LEAF_END JIT_GetSharedNonGCStaticBaseNoCtor_SingleAppDomain, _TEXT // ------------------------------------------------------------------ @@ -638,7 +638,7 @@ LEAF_END JIT_GetSharedNonGCStaticBaseNoCtor_SingleAppDomain, _TEXT LEAF_ENTRY JIT_GetSharedGCStaticBaseNoCtor_SingleAppDomain, _TEXT ld a0, (DomainLocalModule__m_pGCStatics)(a0) - jalr x0, ra, 0 + ret LEAF_END JIT_GetSharedGCStaticBaseNoCtor_SingleAppDomain, _TEXT @@ -745,7 +745,7 @@ NESTED_ENTRY CallEHFunclet, _TEXT, NoHandler ld s11, 152(a2) // Invoke the funclet - jalr ra, a1, 0 + jalr a1 EPILOG_RESTORE_REG_PAIR s1, s2, 16 EPILOG_RESTORE_REG_PAIR s3, s4, 32 @@ -774,7 +774,7 @@ NESTED_ENTRY CallEHFilterFunclet, _TEXT, NoHandler // Save the SP of this function sd fp, 0(a3) // Invoke the filter funclet - jalr ra, a2, 0 + jalr a2 EPILOG_RESTORE_REG_PAIR_INDEXED fp, ra, 16 EPILOG_RETURN @@ -822,7 +822,7 @@ LEAF_ENTRY JIT_GetSharedNonGCStaticBase_SingleAppDomain, _TEXT andi t4, a2, 1 beq t4, zero, LOCAL_LABEL(JIT_GetSharedNonGCStaticBase_SingleAppDomain_CallHelper) - jalr x0, ra, 0 + ret LOCAL_LABEL(JIT_GetSharedNonGCStaticBase_SingleAppDomain_CallHelper): // Tail call JIT_GetSharedNonGCStaticBase_Helper @@ -895,7 +895,7 @@ DynamicHelper DynamicHelperFrameFlags_ObjectArg | DynamicHelperFrameFlags_Object // ------------------------------------------------------------------ LEAF_ENTRY JIT_ProfilerEnterLeaveTailcallStub, _TEXT - jalr x0, ra, 0 + ret LEAF_END JIT_ProfilerEnterLeaveTailcallStub, _TEXT // ------------------------------------------------------------------ diff --git a/src/coreclr/vm/riscv64/calldescrworkerriscv64.S b/src/coreclr/vm/riscv64/calldescrworkerriscv64.S index 38f248fd632d4f..888610f24235fe 100644 --- a/src/coreclr/vm/riscv64/calldescrworkerriscv64.S +++ b/src/coreclr/vm/riscv64/calldescrworkerriscv64.S @@ -69,7 +69,7 @@ LOCAL_LABEL(NoFloatingPoint): ld t4, CallDescrData__pTarget(s1) // call pTarget - jalr ra, 0(t4) + jalr t4 lw a3, CallDescrData__fpReturnSize(s1) @@ -125,71 +125,71 @@ LOCAL_LABEL(NotCorrectReturn): LOCAL_LABEL(FloatReturn): fsw f0, CallDescrData__returnValue(s1) - jal x0, LOCAL_LABEL(ReturnDone) + j LOCAL_LABEL(ReturnDone) LOCAL_LABEL(DoubleReturn): fsd fa0, CallDescrData__returnValue(s1) - jal x0, LOCAL_LABEL(ReturnDone) + j LOCAL_LABEL(ReturnDone) LOCAL_LABEL(FloatIntReturn): fsw fa0, CallDescrData__returnValue(s1) sw a0, (CallDescrData__returnValue + 4)(s1) - jal x0, LOCAL_LABEL(ReturnDone) + j LOCAL_LABEL(ReturnDone) LOCAL_LABEL(DoubleIntReturn): fsd fa0, CallDescrData__returnValue(s1) sw a0, (CallDescrData__returnValue + 8)(s1) - jal x0, LOCAL_LABEL(ReturnDone) + j LOCAL_LABEL(ReturnDone) LOCAL_LABEL(FloatLongReturn): fsw fa0, CallDescrData__returnValue(s1) sd a0, (CallDescrData__returnValue + 8)(s1) - jal x0, LOCAL_LABEL(ReturnDone) + j LOCAL_LABEL(ReturnDone) LOCAL_LABEL(DoubleLongReturn): fsd fa0, CallDescrData__returnValue(s1) sd a0, (CallDescrData__returnValue + 8)(s1) - jal x0, LOCAL_LABEL(ReturnDone) + j LOCAL_LABEL(ReturnDone) LOCAL_LABEL(IntFloatReturn): sw a0, CallDescrData__returnValue(s1) fsw fa0, (CallDescrData__returnValue + 4)(s1) - jal x0, LOCAL_LABEL(ReturnDone) + j LOCAL_LABEL(ReturnDone) LOCAL_LABEL(LongFloatReturn): sd a0, CallDescrData__returnValue(s1) fsw fa0, (CallDescrData__returnValue + 8)(s1) - jal x0, LOCAL_LABEL(ReturnDone) + j LOCAL_LABEL(ReturnDone) LOCAL_LABEL(IntDoubleReturn): sw a0, CallDescrData__returnValue(s1) fsd fa0, (CallDescrData__returnValue + 8)(s1) - jal x0, LOCAL_LABEL(ReturnDone) + j LOCAL_LABEL(ReturnDone) LOCAL_LABEL(LongDoubleReturn): sd a0, CallDescrData__returnValue(s1) fsd fa0, (CallDescrData__returnValue + 8)(s1) - jal x0, LOCAL_LABEL(ReturnDone) + j LOCAL_LABEL(ReturnDone) LOCAL_LABEL(FloatFloatReturn): fsw fa0, CallDescrData__returnValue(s1) fsw fa1, (CallDescrData__returnValue + 4)(s1) - jal x0, LOCAL_LABEL(ReturnDone) + j LOCAL_LABEL(ReturnDone) LOCAL_LABEL(DoubleFloatReturn): fsd fa0, CallDescrData__returnValue(s1) fsw fa1, (CallDescrData__returnValue + 8)(s1) - jal x0, LOCAL_LABEL(ReturnDone) + j LOCAL_LABEL(ReturnDone) LOCAL_LABEL(FloatDoubleReturn): fsw fa0, CallDescrData__returnValue(s1) fsd fa1, (CallDescrData__returnValue + 8)(s1) - jal x0, LOCAL_LABEL(ReturnDone) + j LOCAL_LABEL(ReturnDone) LOCAL_LABEL(DoubleDoubleReturn): fsd fa0, CallDescrData__returnValue(s1) fsd fa1, (CallDescrData__returnValue + 8)(s1) - jal x0, LOCAL_LABEL(ReturnDone) + j LOCAL_LABEL(ReturnDone) LOCAL_LABEL(IntReturn): // Save return value into retbuf for int @@ -201,5 +201,5 @@ LOCAL_LABEL(ReturnDone): EPILOG_STACK_RESTORE EPILOG_RESTORE_REG s1, 16 EPILOG_RESTORE_REG_PAIR_INDEXED fp, ra, 0x20 - jalr x0, ra, 0 + ret NESTED_END CallDescrWorkerInternal, _TEXT diff --git a/src/coreclr/vm/riscv64/cgencpu.h b/src/coreclr/vm/riscv64/cgencpu.h index 3270d2aaa5dffc..23ab1ced88f020 100644 --- a/src/coreclr/vm/riscv64/cgencpu.h +++ b/src/coreclr/vm/riscv64/cgencpu.h @@ -240,7 +240,7 @@ inline void emitJump(LPBYTE pBufferRX, LPBYTE pBufferRW, LPVOID target) // auipc ra, 0 // ld ra, ra, 16 - // jalr r0, ra, 0 + // jalr x0, ra, 0 // nop //padding. pCode[0] = 0x00000097; // auipc ra, 0 diff --git a/src/coreclr/vm/riscv64/crthelpers.S b/src/coreclr/vm/riscv64/crthelpers.S index d4152a96c77015..3151387b3cafd3 100644 --- a/src/coreclr/vm/riscv64/crthelpers.S +++ b/src/coreclr/vm/riscv64/crthelpers.S @@ -17,7 +17,7 @@ LEAF_ENTRY JIT_MemSet, _TEXT tail memset LOCAL_LABEL(JIT_MemSet_ret): - jalr x0, 0(ra) + ret LEAF_END_MARKED JIT_MemSet, _TEXT ////NOTE: Here must use LEAF_END_MARKED! not LEAF_END !!! @@ -30,7 +30,7 @@ LEAF_ENTRY JIT_MemCpy, _TEXT tail memcpy LOCAL_LABEL(JIT_MemCpy_ret): - jalr x0, 0(ra) + ret ////NOTE: Here must use LEAF_END_MARKED! not LEAF_END !!! LEAF_END_MARKED JIT_MemCpy, _TEXT diff --git a/src/coreclr/vm/riscv64/pinvokestubs.S b/src/coreclr/vm/riscv64/pinvokestubs.S index 5df5af6af71f61..b897dbfb0e4b89 100644 --- a/src/coreclr/vm/riscv64/pinvokestubs.S +++ b/src/coreclr/vm/riscv64/pinvokestubs.S @@ -36,7 +36,7 @@ ori \HiddenArg, \HiddenArg, 1 .endif - jalr x0, t0, 0 + jr t0 NESTED_END \__PInvokeStubFuncName, _TEXT NESTED_ENTRY \__PInvokeGenStubFuncName, _TEXT, NoHandler @@ -151,7 +151,7 @@ ld t4, (Frame__m_Next)(a0) sd t4, (Thread_m_pFrame)(a1) - jalr x0, ra, 0 + jr ra LOCAL_LABEL(RarePath): tail JIT_PInvokeEndRarePath diff --git a/src/coreclr/vm/riscv64/stubs.cpp b/src/coreclr/vm/riscv64/stubs.cpp index 1df6b3d6349503..2904f3a22490ea 100644 --- a/src/coreclr/vm/riscv64/stubs.cpp +++ b/src/coreclr/vm/riscv64/stubs.cpp @@ -102,10 +102,10 @@ class BranchInstructionFormat : public InstructionFormat COMPlusThrow(kNotSupportedException); UINT16 imm12 = (UINT16)(0xFFF & dataOffset); - //auipc t1, dataOffset[31:12] - //ld t1, t1, dataOffset[11:0] - //ld t1, t1, 0 - //jalr x0/1, t1,0 + // auipc t1, dataOffset[31:12] + // ld t1, t1, dataOffset[11:0] + // ld t1, t1, 0 + // jalr x0/1, t1,0 *(DWORD*)pOutBufferRW = 0x00000317 | (((dataOffset + 0x800) >> 12) << 12); // auipc t1, dataOffset[31:12] *(DWORD*)(pOutBufferRW + 4) = 0x00033303 | (imm12 << 20); // ld t1, t1, dataOffset[11:0] @@ -131,9 +131,9 @@ class BranchInstructionFormat : public InstructionFormat COMPlusThrow(kNotSupportedException); UINT16 imm12 = (UINT16)(0xFFF & dataOffset); - //auipc t1, dataOffset[31:12] - //ld t1, t1, dataOffset[11:0] - //jalr x0/1, t1,0 + // auipc t1, dataOffset[31:12] + // ld t1, t1, dataOffset[11:0] + // jalr x0/1, t1,0 *(DWORD*)pOutBufferRW = 0x00000317 | (((dataOffset + 0x800) >> 12) << 12);// auipc t1, dataOffset[31:12] *(DWORD*)(pOutBufferRW + 4) = 0x00033303 | (imm12 << 20); // ld t1, t1, dataOffset[11:0] diff --git a/src/coreclr/vm/riscv64/thunktemplates.S b/src/coreclr/vm/riscv64/thunktemplates.S index e078b1a7b989f3..53d81aab388099 100644 --- a/src/coreclr/vm/riscv64/thunktemplates.S +++ b/src/coreclr/vm/riscv64/thunktemplates.S @@ -8,7 +8,7 @@ LEAF_ENTRY StubPrecodeCode auipc t1, 0x1 ld t2, (StubPrecodeData__MethodDesc)(t1) ld t1, (StubPrecodeData__Target)(t1) - jalr x0,t1,0 + jr t1 LEAF_END_MARKED StubPrecodeCode LEAF_ENTRY FixupPrecodeCode @@ -19,7 +19,7 @@ LEAF_ENTRY FixupPrecodeCode auipc t2, 0x1 ld t1, (FixupPrecodeData__PrecodeFixupThunk - 0xa)(t2) ld t2, (FixupPrecodeData__MethodDesc - 0xa)(t2) - jalr x0, t1, 0 + jr t1 LEAF_END_MARKED FixupPrecodeCode LEAF_ENTRY CallCountingStubCode @@ -30,8 +30,8 @@ LEAF_ENTRY CallCountingStubCode sh t1, 0(t3) beq t1, zero, LOCAL_LABEL(CountReachedZero) ld t1, (CallCountingStubData__TargetForMethod)(t2) - jalr x0, 0(t1) + jr t1 LOCAL_LABEL(CountReachedZero): ld t1, (CallCountingStubData__TargetForThresholdReached)(t2) - jalr x0,t1,0 + jr t1 LEAF_END_MARKED CallCountingStubCode From 43d90dc7a59e32dc62f3551c22d9a846d561a763 Mon Sep 17 00:00:00 2001 From: Dong-Heon Jung Date: Fri, 31 Mar 2023 10:32:47 +0900 Subject: [PATCH 09/16] [VM] Fix a bug --- src/coreclr/vm/riscv64/asmhelpers.S | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/coreclr/vm/riscv64/asmhelpers.S b/src/coreclr/vm/riscv64/asmhelpers.S index 852d477f788591..6741f0d3caf37d 100644 --- a/src/coreclr/vm/riscv64/asmhelpers.S +++ b/src/coreclr/vm/riscv64/asmhelpers.S @@ -408,8 +408,8 @@ LEAF_ENTRY HelperMethodFrameRestoreState, _TEXT RestoreRegMS 9, s9 RestoreRegMS 10, s10 RestoreRegMS 11, s11 - RestoreRegMS 12, tp - RestoreRegMS 13, gp + RestoreRegMS 12, gp + RestoreRegMS 13, tp LOCAL_LABEL(Done): // Its imperative that the return value of HelperMethodFrameRestoreState is zero // as it is used in the state machine to loop until it becomes zero. @@ -446,8 +446,8 @@ LEAF_ENTRY LazyMachStateCaptureState, _TEXT sd s9, 72(a1) sd s10, 80(a1) sd s11, 88(a1) - sd tp, 96(a1) - sd gp, 104(a1) + sd gp, 96(a1) + sd tp, 104(a1) ret LEAF_END LazyMachStateCaptureState, _TEXT From cd1ea45de36458a5e3ec66a325bf3e0c4812680d Mon Sep 17 00:00:00 2001 From: Dong-Heon Jung Date: Thu, 6 Apr 2023 13:57:00 +0900 Subject: [PATCH 10/16] [VM] Add getRISCV64PassStructInRegisterFlags --- src/coreclr/inc/corinfo.h | 5 +- src/coreclr/inc/icorjitinfoimpl_generated.h | 3 + .../tools/Common/JitInterface/CorInfoImpl.cs | 6 + .../JitInterface/CorInfoImpl_generated.cs | 122 ++++++----- .../tools/Common/JitInterface/CorInfoTypes.cs | 4 +- .../RISCV64PassStructInRegister.cs | 207 ++++++++++++++++++ .../ThunkGenerator/ThunkInput.txt | 1 + .../ILCompiler.ReadyToRun.csproj | 1 + .../ILCompiler.RyuJit.csproj | 3 + .../aot/jitinterface/jitinterface_generated.h | 10 + .../tools/superpmi/superpmi-shared/lwmlist.h | 1 + .../superpmi-shared/methodcontext.cpp | 25 +++ .../superpmi/superpmi-shared/methodcontext.h | 25 ++- .../superpmi-shim-collector/icorjitinfo.cpp | 8 + .../icorjitinfo_generated.cpp | 7 + .../icorjitinfo_generated.cpp | 6 + .../tools/superpmi/superpmi/icorjitinfo.cpp | 6 + src/coreclr/vm/jitinterface.cpp | 23 +- 18 files changed, 394 insertions(+), 69 deletions(-) create mode 100644 src/coreclr/tools/Common/JitInterface/RISCV64PassStructInRegister.cs diff --git a/src/coreclr/inc/corinfo.h b/src/coreclr/inc/corinfo.h index 28531e00bee5f5..ec5136167b17f7 100644 --- a/src/coreclr/inc/corinfo.h +++ b/src/coreclr/inc/corinfo.h @@ -317,8 +317,8 @@ struct SYSTEMV_AMD64_CORINFO_STRUCT_REG_PASSING_DESCRIPTOR } }; -// StructFloadFieldInfoFlags: used on LoongArch64 architecture by `getLoongArch64PassStructInRegisterFlags` API -// to convey struct argument passing information. +// StructFloadFieldInfoFlags: used on LoongArch64 architecture by `getLoongArch64PassStructInRegisterFlags` and +// `getRISCV64PassStructInRegisterFlags` API to convey struct argument passing information. // // `STRUCT_NO_FLOAT_FIELD` means structs are not passed using the float register(s). // @@ -3000,6 +3000,7 @@ class ICorStaticInfo ) = 0; virtual uint32_t getLoongArch64PassStructInRegisterFlags(CORINFO_CLASS_HANDLE cls) = 0; + virtual uint32_t getRISCV64PassStructInRegisterFlags(CORINFO_CLASS_HANDLE cls) = 0; }; /***************************************************************************** diff --git a/src/coreclr/inc/icorjitinfoimpl_generated.h b/src/coreclr/inc/icorjitinfoimpl_generated.h index c90bb1521f61f6..ecb79d56233038 100644 --- a/src/coreclr/inc/icorjitinfoimpl_generated.h +++ b/src/coreclr/inc/icorjitinfoimpl_generated.h @@ -524,6 +524,9 @@ bool getSystemVAmd64PassStructInRegisterDescriptor( uint32_t getLoongArch64PassStructInRegisterFlags( CORINFO_CLASS_HANDLE structHnd) override; +uint32_t getRISCV64PassStructInRegisterFlags( + CORINFO_CLASS_HANDLE structHnd) override; + uint32_t getThreadTLSIndex( void** ppIndirection) override; diff --git a/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs b/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs index fbb17f280b5e56..fbbcea7b597473 100644 --- a/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs +++ b/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs @@ -3251,6 +3251,12 @@ private uint getLoongArch64PassStructInRegisterFlags(CORINFO_CLASS_STRUCT_* cls) return LoongArch64PassStructInRegister.GetLoongArch64PassStructInRegisterFlags(typeDesc); } + private uint getRISCV64PassStructInRegisterFlags(CORINFO_CLASS_STRUCT_* cls) + { + TypeDesc typeDesc = HandleToObject(cls); + return RISCV64PassStructInRegister.GetRISCV64PassStructInRegisterFlags(typeDesc); + } + private uint getThreadTLSIndex(ref void* ppIndirection) { throw new NotImplementedException("getThreadTLSIndex"); } private void* getInlinedCallFrameVptr(ref void* ppIndirection) diff --git a/src/coreclr/tools/Common/JitInterface/CorInfoImpl_generated.cs b/src/coreclr/tools/Common/JitInterface/CorInfoImpl_generated.cs index 1d0e85e9cdb432..d4fcd158fb2d7f 100644 --- a/src/coreclr/tools/Common/JitInterface/CorInfoImpl_generated.cs +++ b/src/coreclr/tools/Common/JitInterface/CorInfoImpl_generated.cs @@ -1890,6 +1890,21 @@ private static uint _getLoongArch64PassStructInRegisterFlags(IntPtr thisHandle, } } + [UnmanagedCallersOnly] + private static uint _getRISCV64PassStructInRegisterFlags(IntPtr thisHandle, IntPtr* ppException, CORINFO_CLASS_STRUCT_* structHnd) + { + var _this = GetThis(thisHandle); + try + { + return _this.getRISCV64PassStructInRegisterFlags(structHnd); + } + catch (Exception ex) + { + *ppException = _this.AllocException(ex); + return default; + } + } + [UnmanagedCallersOnly] private static uint _getThreadTLSIndex(IntPtr thisHandle, IntPtr* ppException, void** ppIndirection) { @@ -2656,7 +2671,7 @@ private static uint _getJitFlags(IntPtr thisHandle, IntPtr* ppException, CORJIT_ private static IntPtr GetUnmanagedCallbacks() { - void** callbacks = (void**)Marshal.AllocCoTaskMem(sizeof(IntPtr) * 179); + void** callbacks = (void**)Marshal.AllocCoTaskMem(sizeof(IntPtr) * 180); callbacks[0] = (delegate* unmanaged)&_isIntrinsic; callbacks[1] = (delegate* unmanaged)&_getMethodAttribs; @@ -2785,58 +2800,59 @@ private static IntPtr GetUnmanagedCallbacks() callbacks[124] = (delegate* unmanaged)&_findNameOfToken; callbacks[125] = (delegate* unmanaged)&_getSystemVAmd64PassStructInRegisterDescriptor; callbacks[126] = (delegate* unmanaged)&_getLoongArch64PassStructInRegisterFlags; - callbacks[127] = (delegate* unmanaged)&_getThreadTLSIndex; - callbacks[128] = (delegate* unmanaged)&_getInlinedCallFrameVptr; - callbacks[129] = (delegate* unmanaged)&_getAddrOfCaptureThreadGlobal; - callbacks[130] = (delegate* unmanaged)&_getHelperFtn; - callbacks[131] = (delegate* unmanaged)&_getFunctionEntryPoint; - callbacks[132] = (delegate* unmanaged)&_getFunctionFixedEntryPoint; - callbacks[133] = (delegate* unmanaged)&_getMethodSync; - callbacks[134] = (delegate* unmanaged)&_getLazyStringLiteralHelper; - callbacks[135] = (delegate* unmanaged)&_embedModuleHandle; - callbacks[136] = (delegate* unmanaged)&_embedClassHandle; - callbacks[137] = (delegate* unmanaged)&_embedMethodHandle; - callbacks[138] = (delegate* unmanaged)&_embedFieldHandle; - callbacks[139] = (delegate* unmanaged)&_embedGenericHandle; - callbacks[140] = (delegate* unmanaged)&_getLocationOfThisType; - callbacks[141] = (delegate* unmanaged)&_getAddressOfPInvokeTarget; - callbacks[142] = (delegate* unmanaged)&_GetCookieForPInvokeCalliSig; - callbacks[143] = (delegate* unmanaged)&_canGetCookieForPInvokeCalliSig; - callbacks[144] = (delegate* unmanaged)&_getJustMyCodeHandle; - callbacks[145] = (delegate* unmanaged)&_GetProfilingHandle; - callbacks[146] = (delegate* unmanaged)&_getCallInfo; - callbacks[147] = (delegate* unmanaged)&_canAccessFamily; - callbacks[148] = (delegate* unmanaged)&_isRIDClassDomainID; - callbacks[149] = (delegate* unmanaged)&_getClassDomainID; - callbacks[150] = (delegate* unmanaged)&_getReadonlyStaticFieldValue; - callbacks[151] = (delegate* unmanaged)&_getStaticFieldCurrentClass; - callbacks[152] = (delegate* unmanaged)&_getVarArgsHandle; - callbacks[153] = (delegate* unmanaged)&_canGetVarArgsHandle; - callbacks[154] = (delegate* unmanaged)&_constructStringLiteral; - callbacks[155] = (delegate* unmanaged)&_emptyStringLiteral; - callbacks[156] = (delegate* unmanaged)&_getFieldThreadLocalStoreID; - callbacks[157] = (delegate* unmanaged)&_GetDelegateCtor; - callbacks[158] = (delegate* unmanaged)&_MethodCompileComplete; - callbacks[159] = (delegate* unmanaged)&_getTailCallHelpers; - callbacks[160] = (delegate* unmanaged)&_convertPInvokeCalliToCall; - callbacks[161] = (delegate* unmanaged)&_notifyInstructionSetUsage; - callbacks[162] = (delegate* unmanaged)&_updateEntryPointForTailCall; - callbacks[163] = (delegate* unmanaged)&_allocMem; - callbacks[164] = (delegate* unmanaged)&_reserveUnwindInfo; - callbacks[165] = (delegate* unmanaged)&_allocUnwindInfo; - callbacks[166] = (delegate* unmanaged)&_allocGCInfo; - callbacks[167] = (delegate* unmanaged)&_setEHcount; - callbacks[168] = (delegate* unmanaged)&_setEHinfo; - callbacks[169] = (delegate* unmanaged)&_logMsg; - callbacks[170] = (delegate* unmanaged)&_doAssert; - callbacks[171] = (delegate* unmanaged)&_reportFatalError; - callbacks[172] = (delegate* unmanaged)&_getPgoInstrumentationResults; - callbacks[173] = (delegate* unmanaged)&_allocPgoInstrumentationBySchema; - callbacks[174] = (delegate* unmanaged)&_recordCallSite; - callbacks[175] = (delegate* unmanaged)&_recordRelocation; - callbacks[176] = (delegate* unmanaged)&_getRelocTypeHint; - callbacks[177] = (delegate* unmanaged)&_getExpectedTargetArchitecture; - callbacks[178] = (delegate* unmanaged)&_getJitFlags; + callbacks[127] = (delegate* unmanaged)&_getRISCV64PassStructInRegisterFlags; + callbacks[128] = (delegate* unmanaged)&_getThreadTLSIndex; + callbacks[129] = (delegate* unmanaged)&_getInlinedCallFrameVptr; + callbacks[130] = (delegate* unmanaged)&_getAddrOfCaptureThreadGlobal; + callbacks[131] = (delegate* unmanaged)&_getHelperFtn; + callbacks[132] = (delegate* unmanaged)&_getFunctionEntryPoint; + callbacks[133] = (delegate* unmanaged)&_getFunctionFixedEntryPoint; + callbacks[134] = (delegate* unmanaged)&_getMethodSync; + callbacks[135] = (delegate* unmanaged)&_getLazyStringLiteralHelper; + callbacks[136] = (delegate* unmanaged)&_embedModuleHandle; + callbacks[137] = (delegate* unmanaged)&_embedClassHandle; + callbacks[138] = (delegate* unmanaged)&_embedMethodHandle; + callbacks[139] = (delegate* unmanaged)&_embedFieldHandle; + callbacks[140] = (delegate* unmanaged)&_embedGenericHandle; + callbacks[141] = (delegate* unmanaged)&_getLocationOfThisType; + callbacks[142] = (delegate* unmanaged)&_getAddressOfPInvokeTarget; + callbacks[143] = (delegate* unmanaged)&_GetCookieForPInvokeCalliSig; + callbacks[144] = (delegate* unmanaged)&_canGetCookieForPInvokeCalliSig; + callbacks[145] = (delegate* unmanaged)&_getJustMyCodeHandle; + callbacks[146] = (delegate* unmanaged)&_GetProfilingHandle; + callbacks[147] = (delegate* unmanaged)&_getCallInfo; + callbacks[148] = (delegate* unmanaged)&_canAccessFamily; + callbacks[149] = (delegate* unmanaged)&_isRIDClassDomainID; + callbacks[150] = (delegate* unmanaged)&_getClassDomainID; + callbacks[151] = (delegate* unmanaged)&_getReadonlyStaticFieldValue; + callbacks[152] = (delegate* unmanaged)&_getStaticFieldCurrentClass; + callbacks[153] = (delegate* unmanaged)&_getVarArgsHandle; + callbacks[154] = (delegate* unmanaged)&_canGetVarArgsHandle; + callbacks[155] = (delegate* unmanaged)&_constructStringLiteral; + callbacks[156] = (delegate* unmanaged)&_emptyStringLiteral; + callbacks[157] = (delegate* unmanaged)&_getFieldThreadLocalStoreID; + callbacks[158] = (delegate* unmanaged)&_GetDelegateCtor; + callbacks[159] = (delegate* unmanaged)&_MethodCompileComplete; + callbacks[160] = (delegate* unmanaged)&_getTailCallHelpers; + callbacks[161] = (delegate* unmanaged)&_convertPInvokeCalliToCall; + callbacks[162] = (delegate* unmanaged)&_notifyInstructionSetUsage; + callbacks[163] = (delegate* unmanaged)&_updateEntryPointForTailCall; + callbacks[164] = (delegate* unmanaged)&_allocMem; + callbacks[165] = (delegate* unmanaged)&_reserveUnwindInfo; + callbacks[166] = (delegate* unmanaged)&_allocUnwindInfo; + callbacks[167] = (delegate* unmanaged)&_allocGCInfo; + callbacks[168] = (delegate* unmanaged)&_setEHcount; + callbacks[169] = (delegate* unmanaged)&_setEHinfo; + callbacks[170] = (delegate* unmanaged)&_logMsg; + callbacks[171] = (delegate* unmanaged)&_doAssert; + callbacks[172] = (delegate* unmanaged)&_reportFatalError; + callbacks[173] = (delegate* unmanaged)&_getPgoInstrumentationResults; + callbacks[174] = (delegate* unmanaged)&_allocPgoInstrumentationBySchema; + callbacks[175] = (delegate* unmanaged)&_recordCallSite; + callbacks[176] = (delegate* unmanaged)&_recordRelocation; + callbacks[177] = (delegate* unmanaged)&_getRelocTypeHint; + callbacks[178] = (delegate* unmanaged)&_getExpectedTargetArchitecture; + callbacks[179] = (delegate* unmanaged)&_getJitFlags; return (IntPtr)callbacks; } diff --git a/src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs b/src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs index a3d920666ed494..0afa8cf7d0c46a 100644 --- a/src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs +++ b/src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs @@ -1202,8 +1202,8 @@ public struct SYSTEMV_AMD64_CORINFO_STRUCT_REG_PASSING_DESCRIPTOR public byte eightByteOffsets1; }; - // StructFloadFieldInfoFlags: used on LoongArch64 architecture by `getLoongArch64PassStructInRegisterFlags` API - // to convey struct argument passing information. + // StructFloadFieldInfoFlags: used on LoongArch64 architecture by `getLoongArch64PassStructInRegisterFlags` and + // `getRISCV64PassStructInRegisterFlags` API to convey struct argument passing information. // // `STRUCT_NO_FLOAT_FIELD` means structs are not passed using the float register(s). // diff --git a/src/coreclr/tools/Common/JitInterface/RISCV64PassStructInRegister.cs b/src/coreclr/tools/Common/JitInterface/RISCV64PassStructInRegister.cs new file mode 100644 index 00000000000000..a014cb51d016d8 --- /dev/null +++ b/src/coreclr/tools/Common/JitInterface/RISCV64PassStructInRegister.cs @@ -0,0 +1,207 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +using System; +using System.Diagnostics; +using Internal.TypeSystem; + +namespace Internal.JitInterface +{ + + internal static class RISCV64PassStructInRegister + { + public static uint GetRISCV64PassStructInRegisterFlags(TypeDesc typeDesc) + { + FieldDesc firstField = null; + uint floatFieldFlags = (uint)StructFloatFieldInfoFlags.STRUCT_NO_FLOAT_FIELD; + int numIntroducedFields = 0; + foreach (FieldDesc field in typeDesc.GetFields()) + { + if (!field.IsStatic) + { + firstField ??= field; + numIntroducedFields++; + } + } + + if ((numIntroducedFields == 0) || (numIntroducedFields > 2) || (typeDesc.GetElementSize().AsInt > 16)) + { + return (uint)StructFloatFieldInfoFlags.STRUCT_NO_FLOAT_FIELD; + } + + //// The SIMD Intrinsic types are meant to be handled specially and should not be passed as struct registers + if (typeDesc.IsIntrinsic) + { + throw new NotImplementedException("For RISCV64, SIMD would be implemented later"); + } + + MetadataType mdType = typeDesc as MetadataType; + Debug.Assert(mdType != null); + + TypeDesc firstFieldElementType = firstField.FieldType; + int firstFieldSize = firstFieldElementType.GetElementSize().AsInt; + + // A fixed buffer type is always a value type that has exactly one value type field at offset 0 + // and who's size is an exact multiple of the size of the field. + // It is possible that we catch a false positive with this check, but that chance is extremely slim + // and the user can always change their structure to something more descriptive of what they want + // instead of adding additional padding at the end of a one-field structure. + // We do this check here to save looking up the FixedBufferAttribute when loading the field + // from metadata. + bool isFixedBuffer = numIntroducedFields == 1 + && firstFieldElementType.IsValueType + && firstField.Offset.AsInt == 0 + && mdType.HasLayout() + && ((typeDesc.GetElementSize().AsInt % firstFieldSize) == 0); + + if (isFixedBuffer) + { + numIntroducedFields = typeDesc.GetElementSize().AsInt / firstFieldSize; + if (numIntroducedFields > 2) + { + return (uint)StructFloatFieldInfoFlags.STRUCT_NO_FLOAT_FIELD; + } + } + + int fieldIndex = 0; + foreach (FieldDesc field in typeDesc.GetFields()) + { + if (fieldIndex > 1) + { + return (uint)StructFloatFieldInfoFlags.STRUCT_NO_FLOAT_FIELD; + } + else if (field.IsStatic) + { + continue; + } + + Debug.Assert(fieldIndex < numIntroducedFields); + + switch (field.FieldType.Category) + { + case TypeFlags.Double: + { + if (numIntroducedFields == 1) + { + floatFieldFlags = (uint)StructFloatFieldInfoFlags.STRUCT_FLOAT_FIELD_ONLY_ONE; + } + else if (fieldIndex == 0) + { + floatFieldFlags = (uint)StructFloatFieldInfoFlags.STRUCT_FIRST_FIELD_DOUBLE; + } + else if ((floatFieldFlags & (uint)StructFloatFieldInfoFlags.STRUCT_FLOAT_FIELD_FIRST) != 0) + { + floatFieldFlags ^= (uint)StructFloatFieldInfoFlags.STRUCT_MERGE_FIRST_SECOND_8; + } + else + { + floatFieldFlags |= (uint)StructFloatFieldInfoFlags.STRUCT_SECOND_FIELD_DOUBLE; + } + } + break; + + case TypeFlags.Single: + { + if (numIntroducedFields == 1) + { + floatFieldFlags = (uint)StructFloatFieldInfoFlags.STRUCT_FLOAT_FIELD_ONLY_ONE; + } + else if (fieldIndex == 0) + { + floatFieldFlags = (uint)StructFloatFieldInfoFlags.STRUCT_FLOAT_FIELD_FIRST; + } + else if ((floatFieldFlags & (uint)StructFloatFieldInfoFlags.STRUCT_FLOAT_FIELD_FIRST) != 0) + { + floatFieldFlags ^= (uint)StructFloatFieldInfoFlags.STRUCT_MERGE_FIRST_SECOND; + } + else + { + floatFieldFlags |= (uint)StructFloatFieldInfoFlags.STRUCT_FLOAT_FIELD_SECOND; + } + } + break; + + case TypeFlags.ValueType: + //case TypeFlags.Class: + //case TypeFlags.Array: + //case TypeFlags.SzArray: + { + uint floatFieldFlags2 = GetRISCV64PassStructInRegisterFlags(field.FieldType); + if (numIntroducedFields == 1) + { + floatFieldFlags = floatFieldFlags2; + } + else if (field.FieldType.GetElementSize().AsInt > 8) + { + return (uint)StructFloatFieldInfoFlags.STRUCT_NO_FLOAT_FIELD; + } + else if (fieldIndex == 0) + { + if ((floatFieldFlags2 & (uint)StructFloatFieldInfoFlags.STRUCT_FLOAT_FIELD_ONLY_ONE) != 0) + { + floatFieldFlags = (uint)StructFloatFieldInfoFlags.STRUCT_FLOAT_FIELD_FIRST; + } + if (field.FieldType.GetElementSize().AsInt == 8) + { + floatFieldFlags |= (uint)StructFloatFieldInfoFlags.STRUCT_FIRST_FIELD_SIZE_IS8; + } + } + else + { + Debug.Assert(fieldIndex == 1); + if ((floatFieldFlags2 & (uint)StructFloatFieldInfoFlags.STRUCT_FLOAT_FIELD_ONLY_ONE) != 0) + { + floatFieldFlags |= (uint)StructFloatFieldInfoFlags.STRUCT_MERGE_FIRST_SECOND; + } + if (field.FieldType.GetElementSize().AsInt == 8) + { + floatFieldFlags |= (uint)StructFloatFieldInfoFlags.STRUCT_SECOND_FIELD_SIZE_IS8; + } + + floatFieldFlags2 = floatFieldFlags & ((uint)StructFloatFieldInfoFlags.STRUCT_FLOAT_FIELD_FIRST | (uint)StructFloatFieldInfoFlags.STRUCT_FLOAT_FIELD_SECOND); + if (floatFieldFlags2 == 0) + { + floatFieldFlags = (uint)StructFloatFieldInfoFlags.STRUCT_NO_FLOAT_FIELD; + } + else if (floatFieldFlags2 == ((uint)StructFloatFieldInfoFlags.STRUCT_FLOAT_FIELD_FIRST | (uint)StructFloatFieldInfoFlags.STRUCT_FLOAT_FIELD_SECOND)) + { + floatFieldFlags ^= ((uint)StructFloatFieldInfoFlags.STRUCT_FLOAT_FIELD_ONLY_TWO | (uint)StructFloatFieldInfoFlags.STRUCT_FLOAT_FIELD_FIRST | (uint)StructFloatFieldInfoFlags.STRUCT_FLOAT_FIELD_SECOND); + } + } + } + break; + + default: + { + if (field.FieldType.GetElementSize().AsInt == 8) + { + if (numIntroducedFields > 1) + { + if (fieldIndex == 0) + { + floatFieldFlags = (uint)StructFloatFieldInfoFlags.STRUCT_FIRST_FIELD_SIZE_IS8; + } + else if ((floatFieldFlags & (uint)StructFloatFieldInfoFlags.STRUCT_FLOAT_FIELD_FIRST) != 0) + { + floatFieldFlags |= (uint)StructFloatFieldInfoFlags.STRUCT_SECOND_FIELD_SIZE_IS8; + } + else + { + floatFieldFlags = (uint)StructFloatFieldInfoFlags.STRUCT_NO_FLOAT_FIELD; + } + } + } + else if (fieldIndex == 1) + { + floatFieldFlags = (floatFieldFlags & (uint)StructFloatFieldInfoFlags.STRUCT_FLOAT_FIELD_FIRST) > 0 ? floatFieldFlags : (uint)StructFloatFieldInfoFlags.STRUCT_NO_FLOAT_FIELD; + } + break; + } + } + + fieldIndex++; + } + + return floatFieldFlags; + } + } +} diff --git a/src/coreclr/tools/Common/JitInterface/ThunkGenerator/ThunkInput.txt b/src/coreclr/tools/Common/JitInterface/ThunkGenerator/ThunkInput.txt index 96875280c0800c..fa2a79e2c856ee 100644 --- a/src/coreclr/tools/Common/JitInterface/ThunkGenerator/ThunkInput.txt +++ b/src/coreclr/tools/Common/JitInterface/ThunkGenerator/ThunkInput.txt @@ -284,6 +284,7 @@ FUNCTIONS size_t findNameOfToken(CORINFO_MODULE_HANDLE moduleHandle,mdToken token, char * szFQName,size_t FQNameCapacity); bool getSystemVAmd64PassStructInRegisterDescriptor(CORINFO_CLASS_HANDLE structHnd, SYSTEMV_AMD64_CORINFO_STRUCT_REG_PASSING_DESCRIPTOR* structPassInRegDescPtr); uint32_t getLoongArch64PassStructInRegisterFlags(CORINFO_CLASS_HANDLE structHnd); + uint32_t getRISCV64PassStructInRegisterFlags(CORINFO_CLASS_HANDLE structHnd); uint32_t getThreadTLSIndex(void **ppIndirection); const void * getInlinedCallFrameVptr(void **ppIndirection); int32_t * getAddrOfCaptureThreadGlobal(void **ppIndirection); diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/ILCompiler.ReadyToRun.csproj b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/ILCompiler.ReadyToRun.csproj index 6096d476c806c9..642b19544133fc 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/ILCompiler.ReadyToRun.csproj +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/ILCompiler.ReadyToRun.csproj @@ -125,6 +125,7 @@ + diff --git a/src/coreclr/tools/aot/ILCompiler.RyuJit/ILCompiler.RyuJit.csproj b/src/coreclr/tools/aot/ILCompiler.RyuJit/ILCompiler.RyuJit.csproj index 9eeeab38d17abc..4e1c8859136abe 100644 --- a/src/coreclr/tools/aot/ILCompiler.RyuJit/ILCompiler.RyuJit.csproj +++ b/src/coreclr/tools/aot/ILCompiler.RyuJit/ILCompiler.RyuJit.csproj @@ -88,6 +88,9 @@ JitInterface\LoongArch64PassStructInRegister.cs + + JitInterface\RISCV64PassStructInRegister.cs + Pgo\TypeSystemEntityOrUnknown.cs diff --git a/src/coreclr/tools/aot/jitinterface/jitinterface_generated.h b/src/coreclr/tools/aot/jitinterface/jitinterface_generated.h index 2b416b5fccf09a..7b9e92823d94c4 100644 --- a/src/coreclr/tools/aot/jitinterface/jitinterface_generated.h +++ b/src/coreclr/tools/aot/jitinterface/jitinterface_generated.h @@ -138,6 +138,7 @@ struct JitInterfaceCallbacks size_t (* findNameOfToken)(void * thisHandle, CorInfoExceptionClass** ppException, CORINFO_MODULE_HANDLE moduleHandle, unsigned int token, char* szFQName, size_t FQNameCapacity); bool (* getSystemVAmd64PassStructInRegisterDescriptor)(void * thisHandle, CorInfoExceptionClass** ppException, CORINFO_CLASS_HANDLE structHnd, SYSTEMV_AMD64_CORINFO_STRUCT_REG_PASSING_DESCRIPTOR* structPassInRegDescPtr); uint32_t (* getLoongArch64PassStructInRegisterFlags)(void * thisHandle, CorInfoExceptionClass** ppException, CORINFO_CLASS_HANDLE structHnd); + uint32_t (* getRISCV64PassStructInRegisterFlags)(void * thisHandle, CorInfoExceptionClass** ppException, CORINFO_CLASS_HANDLE structHnd); uint32_t (* getThreadTLSIndex)(void * thisHandle, CorInfoExceptionClass** ppException, void** ppIndirection); const void* (* getInlinedCallFrameVptr)(void * thisHandle, CorInfoExceptionClass** ppException, void** ppIndirection); int32_t* (* getAddrOfCaptureThreadGlobal)(void * thisHandle, CorInfoExceptionClass** ppException, void** ppIndirection); @@ -1425,6 +1426,15 @@ class JitInterfaceWrapper : public ICorJitInfo return temp; } + virtual uint32_t getRISCV64PassStructInRegisterFlags( + CORINFO_CLASS_HANDLE structHnd) +{ + CorInfoExceptionClass* pException = nullptr; + uint32_t temp = _callbacks->getRISCV64PassStructInRegisterFlags(_thisHandle, &pException, structHnd); + if (pException != nullptr) throw pException; + return temp; +} + virtual uint32_t getThreadTLSIndex( void** ppIndirection) { diff --git a/src/coreclr/tools/superpmi/superpmi-shared/lwmlist.h b/src/coreclr/tools/superpmi/superpmi-shared/lwmlist.h index e602fba1839517..72da671ca974dd 100644 --- a/src/coreclr/tools/superpmi/superpmi-shared/lwmlist.h +++ b/src/coreclr/tools/superpmi/superpmi-shared/lwmlist.h @@ -124,6 +124,7 @@ LWM(GetSharedCCtorHelper, DWORDLONG, DWORD) LWM(GetStringConfigValue, DWORD, DWORD) LWM(GetSystemVAmd64PassStructInRegisterDescriptor, DWORDLONG, Agnostic_GetSystemVAmd64PassStructInRegisterDescriptor) LWM(GetLoongArch64PassStructInRegisterFlags, DWORDLONG, DWORD) +LWM(GetRISCV64PassStructInRegisterFlags, DWORDLONG, DWORD) LWM(GetTailCallHelpers, Agnostic_GetTailCallHelpers, Agnostic_CORINFO_TAILCALL_HELPERS) LWM(UpdateEntryPointForTailCall, Agnostic_CORINFO_CONST_LOOKUP, Agnostic_CORINFO_CONST_LOOKUP) LWM(GetThreadTLSIndex, DWORD, DLD) diff --git a/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.cpp b/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.cpp index 4da0b172f05ba9..8646708e0ec6a5 100644 --- a/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.cpp +++ b/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.cpp @@ -6118,6 +6118,31 @@ DWORD MethodContext::repGetLoongArch64PassStructInRegisterFlags(CORINFO_CLASS_HA return value; } +void MethodContext::recGetRISCV64PassStructInRegisterFlags(CORINFO_CLASS_HANDLE structHnd, DWORD value) +{ + if (GetRISCV64PassStructInRegisterFlags == nullptr) + GetRISCV64PassStructInRegisterFlags = new LightWeightMap(); + + DWORDLONG key = CastHandle(structHnd); + + GetRISCV64PassStructInRegisterFlags->Add(key, value); + DEBUG_REC(dmpGetRISCV64PassStructInRegisterFlags(key, value)); +} + +void MethodContext::dmpGetRISCV64PassStructInRegisterFlags(DWORDLONG key, DWORD value) +{ + printf("GetRISCV64PassStructInRegisterFlags key %016" PRIX64 " value-%08X", key, value); +} + +DWORD MethodContext::repGetRISCV64PassStructInRegisterFlags(CORINFO_CLASS_HANDLE structHnd) +{ + DWORDLONG key = CastHandle(structHnd); + + DWORD value = LookupByKeyOrMissNoMessage(GetRISCV64PassStructInRegisterFlags, key); + DEBUG_REP(dmpGetRISCV64PassStructInRegisterFlags(key, value)); + return value; +} + void MethodContext::recGetRelocTypeHint(void* target, WORD result) { if (GetRelocTypeHint == nullptr) diff --git a/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.h b/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.h index 920bdbe7a2e65f..bda891866ed27a 100644 --- a/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.h +++ b/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.h @@ -787,6 +787,10 @@ class MethodContext void dmpGetLoongArch64PassStructInRegisterFlags(DWORDLONG key, DWORD value); DWORD repGetLoongArch64PassStructInRegisterFlags(CORINFO_CLASS_HANDLE structHnd); + void recGetRISCV64PassStructInRegisterFlags(CORINFO_CLASS_HANDLE structHnd, DWORD value); + void dmpGetRISCV64PassStructInRegisterFlags(DWORDLONG key, DWORD value); + DWORD repGetRISCV64PassStructInRegisterFlags(CORINFO_CLASS_HANDLE structHnd); + void recGetRelocTypeHint(void* target, WORD result); void dmpGetRelocTypeHint(DWORDLONG key, DWORD value); WORD repGetRelocTypeHint(void* target); @@ -1152,16 +1156,17 @@ enum mcPackets Packet_IsIntrinsic = 192, Packet_UpdateEntryPointForTailCall = 193, Packet_GetLoongArch64PassStructInRegisterFlags = 194, - Packet_GetExactClasses = 195, - Packet_GetRuntimeTypePointer = 196, - Packet_PrintObjectDescription = 197, - Packet_GetReadonlyStaticFieldValue = 198, - Packet_GetObjectType = 199, - Packet_IsObjectImmutable = 200, - Packet_ExpandRawHandleIntrinsic = 201, - Packet_GetArrayOrStringLength = 202, - Packet_IsEnum = 203, - Packet_GetStringChar = 204, + Packet_GetRISCV64PassStructInRegisterFlags = 195, + Packet_GetExactClasses = 196, + Packet_GetRuntimeTypePointer = 197, + Packet_PrintObjectDescription = 198, + Packet_GetReadonlyStaticFieldValue = 199, + Packet_GetObjectType = 200, + Packet_IsObjectImmutable = 201, + Packet_ExpandRawHandleIntrinsic = 202, + Packet_GetArrayOrStringLength = 203, + Packet_IsEnum = 204, + Packet_GetStringChar = 205, }; void SetDebugDumpVariables(); diff --git a/src/coreclr/tools/superpmi/superpmi-shim-collector/icorjitinfo.cpp b/src/coreclr/tools/superpmi/superpmi-shim-collector/icorjitinfo.cpp index 02f70a5ed9861e..1119921b664b45 100644 --- a/src/coreclr/tools/superpmi/superpmi-shim-collector/icorjitinfo.cpp +++ b/src/coreclr/tools/superpmi/superpmi-shim-collector/icorjitinfo.cpp @@ -1454,6 +1454,14 @@ uint32_t interceptor_ICJI::getLoongArch64PassStructInRegisterFlags(CORINFO_CLASS return temp; } +uint32_t interceptor_ICJI::getRISCV64PassStructInRegisterFlags(CORINFO_CLASS_HANDLE structHnd) +{ + mc->cr->AddCall("getRISCV64PassStructInRegisterFlags"); + uint32_t temp = original_ICorJitInfo->getRISCV64PassStructInRegisterFlags(structHnd); + mc->recGetRISCV64PassStructInRegisterFlags(structHnd, temp); + return temp; +} + // Stuff on ICorDynamicInfo uint32_t interceptor_ICJI::getThreadTLSIndex(void** ppIndirection) { diff --git a/src/coreclr/tools/superpmi/superpmi-shim-counter/icorjitinfo_generated.cpp b/src/coreclr/tools/superpmi/superpmi-shim-counter/icorjitinfo_generated.cpp index 9a30ebd097346d..20bc281a581759 100644 --- a/src/coreclr/tools/superpmi/superpmi-shim-counter/icorjitinfo_generated.cpp +++ b/src/coreclr/tools/superpmi/superpmi-shim-counter/icorjitinfo_generated.cpp @@ -1023,6 +1023,13 @@ uint32_t interceptor_ICJI::getLoongArch64PassStructInRegisterFlags( return original_ICorJitInfo->getLoongArch64PassStructInRegisterFlags(structHnd); } +uint32_t interceptor_ICJI::getRISCV64PassStructInRegisterFlags( + CORINFO_CLASS_HANDLE structHnd) +{ + mcs->AddCall("getRISCV64PassStructInRegisterFlags"); + return original_ICorJitInfo->getRISCV64PassStructInRegisterFlags(structHnd); +} + uint32_t interceptor_ICJI::getThreadTLSIndex( void** ppIndirection) { diff --git a/src/coreclr/tools/superpmi/superpmi-shim-simple/icorjitinfo_generated.cpp b/src/coreclr/tools/superpmi/superpmi-shim-simple/icorjitinfo_generated.cpp index 5c81284fc94105..cb54b4f75d2797 100644 --- a/src/coreclr/tools/superpmi/superpmi-shim-simple/icorjitinfo_generated.cpp +++ b/src/coreclr/tools/superpmi/superpmi-shim-simple/icorjitinfo_generated.cpp @@ -896,6 +896,12 @@ uint32_t interceptor_ICJI::getLoongArch64PassStructInRegisterFlags( return original_ICorJitInfo->getLoongArch64PassStructInRegisterFlags(structHnd); } +uint32_t interceptor_ICJI::getRISCV64PassStructInRegisterFlags( + CORINFO_CLASS_HANDLE structHnd) +{ + return original_ICorJitInfo->getRISCV64PassStructInRegisterFlags(structHnd); +} + uint32_t interceptor_ICJI::getThreadTLSIndex( void** ppIndirection) { diff --git a/src/coreclr/tools/superpmi/superpmi/icorjitinfo.cpp b/src/coreclr/tools/superpmi/superpmi/icorjitinfo.cpp index c627a042d4f354..43d2fda34eed9e 100644 --- a/src/coreclr/tools/superpmi/superpmi/icorjitinfo.cpp +++ b/src/coreclr/tools/superpmi/superpmi/icorjitinfo.cpp @@ -1272,6 +1272,12 @@ uint32_t MyICJI::getLoongArch64PassStructInRegisterFlags(CORINFO_CLASS_HANDLE st return jitInstance->mc->repGetLoongArch64PassStructInRegisterFlags(structHnd); } +uint32_t MyICJI::getRISCV64PassStructInRegisterFlags(CORINFO_CLASS_HANDLE structHnd) +{ + jitInstance->mc->cr->AddCall("getRISCV64PassStructInRegisterFlags"); + return jitInstance->mc->repGetRISCV64PassStructInRegisterFlags(structHnd); +} + // Stuff on ICorDynamicInfo uint32_t MyICJI::getThreadTLSIndex(void** ppIndirection) { diff --git a/src/coreclr/vm/jitinterface.cpp b/src/coreclr/vm/jitinterface.cpp index 9533a927e3f3ef..3dd3edd6c3807e 100644 --- a/src/coreclr/vm/jitinterface.cpp +++ b/src/coreclr/vm/jitinterface.cpp @@ -9439,8 +9439,6 @@ uint32_t CEEInfo::getLoongArch64PassStructInRegisterFlags(CORINFO_CLASS_HANDLE c #if defined(TARGET_LOONGARCH64) size = (uint32_t)MethodTable::GetLoongArch64PassStructInRegisterFlags(cls); -#elif defined(TARGET_RISCV64) - size = (uint32_t)MethodTable::GetRiscv64PassStructInRegisterFlags(cls); #endif EE_TO_JIT_TRANSITION_LEAF(); @@ -9448,6 +9446,27 @@ uint32_t CEEInfo::getLoongArch64PassStructInRegisterFlags(CORINFO_CLASS_HANDLE c return size; } +uint32_t CEEInfo::getRISCV64PassStructInRegisterFlags(CORINFO_CLASS_HANDLE cls) +{ + CONTRACTL { + NOTHROW; + GC_NOTRIGGER; + MODE_PREEMPTIVE; + } CONTRACTL_END; + + JIT_TO_EE_TRANSITION_LEAF(); + + uint32_t size = STRUCT_NO_FLOAT_FIELD; + +#if defined(TARGET_RISCV64) + size = (uint32_t)MethodTable::GetRiscv64PassStructInRegisterFlags(cls); +#endif // TARGET_RISCV64 + + EE_TO_JIT_TRANSITION_LEAF(); + + return size; +} + /*********************************************************************/ int CEEInfo::getExactClasses ( From 9d675f66e24d8608fe92741323a766934c821086 Mon Sep 17 00:00:00 2001 From: Dong-Heon Jung Date: Thu, 6 Apr 2023 19:08:52 +0900 Subject: [PATCH 11/16] Revert "[VM] Add getRISCV64PassStructInRegisterFlags" This reverts commit cd1ea45de36458a5e3ec66a325bf3e0c4812680d. --- src/coreclr/inc/corinfo.h | 5 +- src/coreclr/inc/icorjitinfoimpl_generated.h | 3 - .../tools/Common/JitInterface/CorInfoImpl.cs | 6 - .../JitInterface/CorInfoImpl_generated.cs | 122 +++++------ .../tools/Common/JitInterface/CorInfoTypes.cs | 4 +- .../RISCV64PassStructInRegister.cs | 207 ------------------ .../ThunkGenerator/ThunkInput.txt | 1 - .../ILCompiler.ReadyToRun.csproj | 1 - .../ILCompiler.RyuJit.csproj | 3 - .../aot/jitinterface/jitinterface_generated.h | 10 - .../tools/superpmi/superpmi-shared/lwmlist.h | 1 - .../superpmi-shared/methodcontext.cpp | 25 --- .../superpmi/superpmi-shared/methodcontext.h | 25 +-- .../superpmi-shim-collector/icorjitinfo.cpp | 8 - .../icorjitinfo_generated.cpp | 7 - .../icorjitinfo_generated.cpp | 6 - .../tools/superpmi/superpmi/icorjitinfo.cpp | 6 - src/coreclr/vm/jitinterface.cpp | 23 +- 18 files changed, 69 insertions(+), 394 deletions(-) delete mode 100644 src/coreclr/tools/Common/JitInterface/RISCV64PassStructInRegister.cs diff --git a/src/coreclr/inc/corinfo.h b/src/coreclr/inc/corinfo.h index ec5136167b17f7..28531e00bee5f5 100644 --- a/src/coreclr/inc/corinfo.h +++ b/src/coreclr/inc/corinfo.h @@ -317,8 +317,8 @@ struct SYSTEMV_AMD64_CORINFO_STRUCT_REG_PASSING_DESCRIPTOR } }; -// StructFloadFieldInfoFlags: used on LoongArch64 architecture by `getLoongArch64PassStructInRegisterFlags` and -// `getRISCV64PassStructInRegisterFlags` API to convey struct argument passing information. +// StructFloadFieldInfoFlags: used on LoongArch64 architecture by `getLoongArch64PassStructInRegisterFlags` API +// to convey struct argument passing information. // // `STRUCT_NO_FLOAT_FIELD` means structs are not passed using the float register(s). // @@ -3000,7 +3000,6 @@ class ICorStaticInfo ) = 0; virtual uint32_t getLoongArch64PassStructInRegisterFlags(CORINFO_CLASS_HANDLE cls) = 0; - virtual uint32_t getRISCV64PassStructInRegisterFlags(CORINFO_CLASS_HANDLE cls) = 0; }; /***************************************************************************** diff --git a/src/coreclr/inc/icorjitinfoimpl_generated.h b/src/coreclr/inc/icorjitinfoimpl_generated.h index ecb79d56233038..c90bb1521f61f6 100644 --- a/src/coreclr/inc/icorjitinfoimpl_generated.h +++ b/src/coreclr/inc/icorjitinfoimpl_generated.h @@ -524,9 +524,6 @@ bool getSystemVAmd64PassStructInRegisterDescriptor( uint32_t getLoongArch64PassStructInRegisterFlags( CORINFO_CLASS_HANDLE structHnd) override; -uint32_t getRISCV64PassStructInRegisterFlags( - CORINFO_CLASS_HANDLE structHnd) override; - uint32_t getThreadTLSIndex( void** ppIndirection) override; diff --git a/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs b/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs index fbbcea7b597473..fbb17f280b5e56 100644 --- a/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs +++ b/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs @@ -3251,12 +3251,6 @@ private uint getLoongArch64PassStructInRegisterFlags(CORINFO_CLASS_STRUCT_* cls) return LoongArch64PassStructInRegister.GetLoongArch64PassStructInRegisterFlags(typeDesc); } - private uint getRISCV64PassStructInRegisterFlags(CORINFO_CLASS_STRUCT_* cls) - { - TypeDesc typeDesc = HandleToObject(cls); - return RISCV64PassStructInRegister.GetRISCV64PassStructInRegisterFlags(typeDesc); - } - private uint getThreadTLSIndex(ref void* ppIndirection) { throw new NotImplementedException("getThreadTLSIndex"); } private void* getInlinedCallFrameVptr(ref void* ppIndirection) diff --git a/src/coreclr/tools/Common/JitInterface/CorInfoImpl_generated.cs b/src/coreclr/tools/Common/JitInterface/CorInfoImpl_generated.cs index d4fcd158fb2d7f..1d0e85e9cdb432 100644 --- a/src/coreclr/tools/Common/JitInterface/CorInfoImpl_generated.cs +++ b/src/coreclr/tools/Common/JitInterface/CorInfoImpl_generated.cs @@ -1890,21 +1890,6 @@ private static uint _getLoongArch64PassStructInRegisterFlags(IntPtr thisHandle, } } - [UnmanagedCallersOnly] - private static uint _getRISCV64PassStructInRegisterFlags(IntPtr thisHandle, IntPtr* ppException, CORINFO_CLASS_STRUCT_* structHnd) - { - var _this = GetThis(thisHandle); - try - { - return _this.getRISCV64PassStructInRegisterFlags(structHnd); - } - catch (Exception ex) - { - *ppException = _this.AllocException(ex); - return default; - } - } - [UnmanagedCallersOnly] private static uint _getThreadTLSIndex(IntPtr thisHandle, IntPtr* ppException, void** ppIndirection) { @@ -2671,7 +2656,7 @@ private static uint _getJitFlags(IntPtr thisHandle, IntPtr* ppException, CORJIT_ private static IntPtr GetUnmanagedCallbacks() { - void** callbacks = (void**)Marshal.AllocCoTaskMem(sizeof(IntPtr) * 180); + void** callbacks = (void**)Marshal.AllocCoTaskMem(sizeof(IntPtr) * 179); callbacks[0] = (delegate* unmanaged)&_isIntrinsic; callbacks[1] = (delegate* unmanaged)&_getMethodAttribs; @@ -2800,59 +2785,58 @@ private static IntPtr GetUnmanagedCallbacks() callbacks[124] = (delegate* unmanaged)&_findNameOfToken; callbacks[125] = (delegate* unmanaged)&_getSystemVAmd64PassStructInRegisterDescriptor; callbacks[126] = (delegate* unmanaged)&_getLoongArch64PassStructInRegisterFlags; - callbacks[127] = (delegate* unmanaged)&_getRISCV64PassStructInRegisterFlags; - callbacks[128] = (delegate* unmanaged)&_getThreadTLSIndex; - callbacks[129] = (delegate* unmanaged)&_getInlinedCallFrameVptr; - callbacks[130] = (delegate* unmanaged)&_getAddrOfCaptureThreadGlobal; - callbacks[131] = (delegate* unmanaged)&_getHelperFtn; - callbacks[132] = (delegate* unmanaged)&_getFunctionEntryPoint; - callbacks[133] = (delegate* unmanaged)&_getFunctionFixedEntryPoint; - callbacks[134] = (delegate* unmanaged)&_getMethodSync; - callbacks[135] = (delegate* unmanaged)&_getLazyStringLiteralHelper; - callbacks[136] = (delegate* unmanaged)&_embedModuleHandle; - callbacks[137] = (delegate* unmanaged)&_embedClassHandle; - callbacks[138] = (delegate* unmanaged)&_embedMethodHandle; - callbacks[139] = (delegate* unmanaged)&_embedFieldHandle; - callbacks[140] = (delegate* unmanaged)&_embedGenericHandle; - callbacks[141] = (delegate* unmanaged)&_getLocationOfThisType; - callbacks[142] = (delegate* unmanaged)&_getAddressOfPInvokeTarget; - callbacks[143] = (delegate* unmanaged)&_GetCookieForPInvokeCalliSig; - callbacks[144] = (delegate* unmanaged)&_canGetCookieForPInvokeCalliSig; - callbacks[145] = (delegate* unmanaged)&_getJustMyCodeHandle; - callbacks[146] = (delegate* unmanaged)&_GetProfilingHandle; - callbacks[147] = (delegate* unmanaged)&_getCallInfo; - callbacks[148] = (delegate* unmanaged)&_canAccessFamily; - callbacks[149] = (delegate* unmanaged)&_isRIDClassDomainID; - callbacks[150] = (delegate* unmanaged)&_getClassDomainID; - callbacks[151] = (delegate* unmanaged)&_getReadonlyStaticFieldValue; - callbacks[152] = (delegate* unmanaged)&_getStaticFieldCurrentClass; - callbacks[153] = (delegate* unmanaged)&_getVarArgsHandle; - callbacks[154] = (delegate* unmanaged)&_canGetVarArgsHandle; - callbacks[155] = (delegate* unmanaged)&_constructStringLiteral; - callbacks[156] = (delegate* unmanaged)&_emptyStringLiteral; - callbacks[157] = (delegate* unmanaged)&_getFieldThreadLocalStoreID; - callbacks[158] = (delegate* unmanaged)&_GetDelegateCtor; - callbacks[159] = (delegate* unmanaged)&_MethodCompileComplete; - callbacks[160] = (delegate* unmanaged)&_getTailCallHelpers; - callbacks[161] = (delegate* unmanaged)&_convertPInvokeCalliToCall; - callbacks[162] = (delegate* unmanaged)&_notifyInstructionSetUsage; - callbacks[163] = (delegate* unmanaged)&_updateEntryPointForTailCall; - callbacks[164] = (delegate* unmanaged)&_allocMem; - callbacks[165] = (delegate* unmanaged)&_reserveUnwindInfo; - callbacks[166] = (delegate* unmanaged)&_allocUnwindInfo; - callbacks[167] = (delegate* unmanaged)&_allocGCInfo; - callbacks[168] = (delegate* unmanaged)&_setEHcount; - callbacks[169] = (delegate* unmanaged)&_setEHinfo; - callbacks[170] = (delegate* unmanaged)&_logMsg; - callbacks[171] = (delegate* unmanaged)&_doAssert; - callbacks[172] = (delegate* unmanaged)&_reportFatalError; - callbacks[173] = (delegate* unmanaged)&_getPgoInstrumentationResults; - callbacks[174] = (delegate* unmanaged)&_allocPgoInstrumentationBySchema; - callbacks[175] = (delegate* unmanaged)&_recordCallSite; - callbacks[176] = (delegate* unmanaged)&_recordRelocation; - callbacks[177] = (delegate* unmanaged)&_getRelocTypeHint; - callbacks[178] = (delegate* unmanaged)&_getExpectedTargetArchitecture; - callbacks[179] = (delegate* unmanaged)&_getJitFlags; + callbacks[127] = (delegate* unmanaged)&_getThreadTLSIndex; + callbacks[128] = (delegate* unmanaged)&_getInlinedCallFrameVptr; + callbacks[129] = (delegate* unmanaged)&_getAddrOfCaptureThreadGlobal; + callbacks[130] = (delegate* unmanaged)&_getHelperFtn; + callbacks[131] = (delegate* unmanaged)&_getFunctionEntryPoint; + callbacks[132] = (delegate* unmanaged)&_getFunctionFixedEntryPoint; + callbacks[133] = (delegate* unmanaged)&_getMethodSync; + callbacks[134] = (delegate* unmanaged)&_getLazyStringLiteralHelper; + callbacks[135] = (delegate* unmanaged)&_embedModuleHandle; + callbacks[136] = (delegate* unmanaged)&_embedClassHandle; + callbacks[137] = (delegate* unmanaged)&_embedMethodHandle; + callbacks[138] = (delegate* unmanaged)&_embedFieldHandle; + callbacks[139] = (delegate* unmanaged)&_embedGenericHandle; + callbacks[140] = (delegate* unmanaged)&_getLocationOfThisType; + callbacks[141] = (delegate* unmanaged)&_getAddressOfPInvokeTarget; + callbacks[142] = (delegate* unmanaged)&_GetCookieForPInvokeCalliSig; + callbacks[143] = (delegate* unmanaged)&_canGetCookieForPInvokeCalliSig; + callbacks[144] = (delegate* unmanaged)&_getJustMyCodeHandle; + callbacks[145] = (delegate* unmanaged)&_GetProfilingHandle; + callbacks[146] = (delegate* unmanaged)&_getCallInfo; + callbacks[147] = (delegate* unmanaged)&_canAccessFamily; + callbacks[148] = (delegate* unmanaged)&_isRIDClassDomainID; + callbacks[149] = (delegate* unmanaged)&_getClassDomainID; + callbacks[150] = (delegate* unmanaged)&_getReadonlyStaticFieldValue; + callbacks[151] = (delegate* unmanaged)&_getStaticFieldCurrentClass; + callbacks[152] = (delegate* unmanaged)&_getVarArgsHandle; + callbacks[153] = (delegate* unmanaged)&_canGetVarArgsHandle; + callbacks[154] = (delegate* unmanaged)&_constructStringLiteral; + callbacks[155] = (delegate* unmanaged)&_emptyStringLiteral; + callbacks[156] = (delegate* unmanaged)&_getFieldThreadLocalStoreID; + callbacks[157] = (delegate* unmanaged)&_GetDelegateCtor; + callbacks[158] = (delegate* unmanaged)&_MethodCompileComplete; + callbacks[159] = (delegate* unmanaged)&_getTailCallHelpers; + callbacks[160] = (delegate* unmanaged)&_convertPInvokeCalliToCall; + callbacks[161] = (delegate* unmanaged)&_notifyInstructionSetUsage; + callbacks[162] = (delegate* unmanaged)&_updateEntryPointForTailCall; + callbacks[163] = (delegate* unmanaged)&_allocMem; + callbacks[164] = (delegate* unmanaged)&_reserveUnwindInfo; + callbacks[165] = (delegate* unmanaged)&_allocUnwindInfo; + callbacks[166] = (delegate* unmanaged)&_allocGCInfo; + callbacks[167] = (delegate* unmanaged)&_setEHcount; + callbacks[168] = (delegate* unmanaged)&_setEHinfo; + callbacks[169] = (delegate* unmanaged)&_logMsg; + callbacks[170] = (delegate* unmanaged)&_doAssert; + callbacks[171] = (delegate* unmanaged)&_reportFatalError; + callbacks[172] = (delegate* unmanaged)&_getPgoInstrumentationResults; + callbacks[173] = (delegate* unmanaged)&_allocPgoInstrumentationBySchema; + callbacks[174] = (delegate* unmanaged)&_recordCallSite; + callbacks[175] = (delegate* unmanaged)&_recordRelocation; + callbacks[176] = (delegate* unmanaged)&_getRelocTypeHint; + callbacks[177] = (delegate* unmanaged)&_getExpectedTargetArchitecture; + callbacks[178] = (delegate* unmanaged)&_getJitFlags; return (IntPtr)callbacks; } diff --git a/src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs b/src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs index 0afa8cf7d0c46a..a3d920666ed494 100644 --- a/src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs +++ b/src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs @@ -1202,8 +1202,8 @@ public struct SYSTEMV_AMD64_CORINFO_STRUCT_REG_PASSING_DESCRIPTOR public byte eightByteOffsets1; }; - // StructFloadFieldInfoFlags: used on LoongArch64 architecture by `getLoongArch64PassStructInRegisterFlags` and - // `getRISCV64PassStructInRegisterFlags` API to convey struct argument passing information. + // StructFloadFieldInfoFlags: used on LoongArch64 architecture by `getLoongArch64PassStructInRegisterFlags` API + // to convey struct argument passing information. // // `STRUCT_NO_FLOAT_FIELD` means structs are not passed using the float register(s). // diff --git a/src/coreclr/tools/Common/JitInterface/RISCV64PassStructInRegister.cs b/src/coreclr/tools/Common/JitInterface/RISCV64PassStructInRegister.cs deleted file mode 100644 index a014cb51d016d8..00000000000000 --- a/src/coreclr/tools/Common/JitInterface/RISCV64PassStructInRegister.cs +++ /dev/null @@ -1,207 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -using System; -using System.Diagnostics; -using Internal.TypeSystem; - -namespace Internal.JitInterface -{ - - internal static class RISCV64PassStructInRegister - { - public static uint GetRISCV64PassStructInRegisterFlags(TypeDesc typeDesc) - { - FieldDesc firstField = null; - uint floatFieldFlags = (uint)StructFloatFieldInfoFlags.STRUCT_NO_FLOAT_FIELD; - int numIntroducedFields = 0; - foreach (FieldDesc field in typeDesc.GetFields()) - { - if (!field.IsStatic) - { - firstField ??= field; - numIntroducedFields++; - } - } - - if ((numIntroducedFields == 0) || (numIntroducedFields > 2) || (typeDesc.GetElementSize().AsInt > 16)) - { - return (uint)StructFloatFieldInfoFlags.STRUCT_NO_FLOAT_FIELD; - } - - //// The SIMD Intrinsic types are meant to be handled specially and should not be passed as struct registers - if (typeDesc.IsIntrinsic) - { - throw new NotImplementedException("For RISCV64, SIMD would be implemented later"); - } - - MetadataType mdType = typeDesc as MetadataType; - Debug.Assert(mdType != null); - - TypeDesc firstFieldElementType = firstField.FieldType; - int firstFieldSize = firstFieldElementType.GetElementSize().AsInt; - - // A fixed buffer type is always a value type that has exactly one value type field at offset 0 - // and who's size is an exact multiple of the size of the field. - // It is possible that we catch a false positive with this check, but that chance is extremely slim - // and the user can always change their structure to something more descriptive of what they want - // instead of adding additional padding at the end of a one-field structure. - // We do this check here to save looking up the FixedBufferAttribute when loading the field - // from metadata. - bool isFixedBuffer = numIntroducedFields == 1 - && firstFieldElementType.IsValueType - && firstField.Offset.AsInt == 0 - && mdType.HasLayout() - && ((typeDesc.GetElementSize().AsInt % firstFieldSize) == 0); - - if (isFixedBuffer) - { - numIntroducedFields = typeDesc.GetElementSize().AsInt / firstFieldSize; - if (numIntroducedFields > 2) - { - return (uint)StructFloatFieldInfoFlags.STRUCT_NO_FLOAT_FIELD; - } - } - - int fieldIndex = 0; - foreach (FieldDesc field in typeDesc.GetFields()) - { - if (fieldIndex > 1) - { - return (uint)StructFloatFieldInfoFlags.STRUCT_NO_FLOAT_FIELD; - } - else if (field.IsStatic) - { - continue; - } - - Debug.Assert(fieldIndex < numIntroducedFields); - - switch (field.FieldType.Category) - { - case TypeFlags.Double: - { - if (numIntroducedFields == 1) - { - floatFieldFlags = (uint)StructFloatFieldInfoFlags.STRUCT_FLOAT_FIELD_ONLY_ONE; - } - else if (fieldIndex == 0) - { - floatFieldFlags = (uint)StructFloatFieldInfoFlags.STRUCT_FIRST_FIELD_DOUBLE; - } - else if ((floatFieldFlags & (uint)StructFloatFieldInfoFlags.STRUCT_FLOAT_FIELD_FIRST) != 0) - { - floatFieldFlags ^= (uint)StructFloatFieldInfoFlags.STRUCT_MERGE_FIRST_SECOND_8; - } - else - { - floatFieldFlags |= (uint)StructFloatFieldInfoFlags.STRUCT_SECOND_FIELD_DOUBLE; - } - } - break; - - case TypeFlags.Single: - { - if (numIntroducedFields == 1) - { - floatFieldFlags = (uint)StructFloatFieldInfoFlags.STRUCT_FLOAT_FIELD_ONLY_ONE; - } - else if (fieldIndex == 0) - { - floatFieldFlags = (uint)StructFloatFieldInfoFlags.STRUCT_FLOAT_FIELD_FIRST; - } - else if ((floatFieldFlags & (uint)StructFloatFieldInfoFlags.STRUCT_FLOAT_FIELD_FIRST) != 0) - { - floatFieldFlags ^= (uint)StructFloatFieldInfoFlags.STRUCT_MERGE_FIRST_SECOND; - } - else - { - floatFieldFlags |= (uint)StructFloatFieldInfoFlags.STRUCT_FLOAT_FIELD_SECOND; - } - } - break; - - case TypeFlags.ValueType: - //case TypeFlags.Class: - //case TypeFlags.Array: - //case TypeFlags.SzArray: - { - uint floatFieldFlags2 = GetRISCV64PassStructInRegisterFlags(field.FieldType); - if (numIntroducedFields == 1) - { - floatFieldFlags = floatFieldFlags2; - } - else if (field.FieldType.GetElementSize().AsInt > 8) - { - return (uint)StructFloatFieldInfoFlags.STRUCT_NO_FLOAT_FIELD; - } - else if (fieldIndex == 0) - { - if ((floatFieldFlags2 & (uint)StructFloatFieldInfoFlags.STRUCT_FLOAT_FIELD_ONLY_ONE) != 0) - { - floatFieldFlags = (uint)StructFloatFieldInfoFlags.STRUCT_FLOAT_FIELD_FIRST; - } - if (field.FieldType.GetElementSize().AsInt == 8) - { - floatFieldFlags |= (uint)StructFloatFieldInfoFlags.STRUCT_FIRST_FIELD_SIZE_IS8; - } - } - else - { - Debug.Assert(fieldIndex == 1); - if ((floatFieldFlags2 & (uint)StructFloatFieldInfoFlags.STRUCT_FLOAT_FIELD_ONLY_ONE) != 0) - { - floatFieldFlags |= (uint)StructFloatFieldInfoFlags.STRUCT_MERGE_FIRST_SECOND; - } - if (field.FieldType.GetElementSize().AsInt == 8) - { - floatFieldFlags |= (uint)StructFloatFieldInfoFlags.STRUCT_SECOND_FIELD_SIZE_IS8; - } - - floatFieldFlags2 = floatFieldFlags & ((uint)StructFloatFieldInfoFlags.STRUCT_FLOAT_FIELD_FIRST | (uint)StructFloatFieldInfoFlags.STRUCT_FLOAT_FIELD_SECOND); - if (floatFieldFlags2 == 0) - { - floatFieldFlags = (uint)StructFloatFieldInfoFlags.STRUCT_NO_FLOAT_FIELD; - } - else if (floatFieldFlags2 == ((uint)StructFloatFieldInfoFlags.STRUCT_FLOAT_FIELD_FIRST | (uint)StructFloatFieldInfoFlags.STRUCT_FLOAT_FIELD_SECOND)) - { - floatFieldFlags ^= ((uint)StructFloatFieldInfoFlags.STRUCT_FLOAT_FIELD_ONLY_TWO | (uint)StructFloatFieldInfoFlags.STRUCT_FLOAT_FIELD_FIRST | (uint)StructFloatFieldInfoFlags.STRUCT_FLOAT_FIELD_SECOND); - } - } - } - break; - - default: - { - if (field.FieldType.GetElementSize().AsInt == 8) - { - if (numIntroducedFields > 1) - { - if (fieldIndex == 0) - { - floatFieldFlags = (uint)StructFloatFieldInfoFlags.STRUCT_FIRST_FIELD_SIZE_IS8; - } - else if ((floatFieldFlags & (uint)StructFloatFieldInfoFlags.STRUCT_FLOAT_FIELD_FIRST) != 0) - { - floatFieldFlags |= (uint)StructFloatFieldInfoFlags.STRUCT_SECOND_FIELD_SIZE_IS8; - } - else - { - floatFieldFlags = (uint)StructFloatFieldInfoFlags.STRUCT_NO_FLOAT_FIELD; - } - } - } - else if (fieldIndex == 1) - { - floatFieldFlags = (floatFieldFlags & (uint)StructFloatFieldInfoFlags.STRUCT_FLOAT_FIELD_FIRST) > 0 ? floatFieldFlags : (uint)StructFloatFieldInfoFlags.STRUCT_NO_FLOAT_FIELD; - } - break; - } - } - - fieldIndex++; - } - - return floatFieldFlags; - } - } -} diff --git a/src/coreclr/tools/Common/JitInterface/ThunkGenerator/ThunkInput.txt b/src/coreclr/tools/Common/JitInterface/ThunkGenerator/ThunkInput.txt index fa2a79e2c856ee..96875280c0800c 100644 --- a/src/coreclr/tools/Common/JitInterface/ThunkGenerator/ThunkInput.txt +++ b/src/coreclr/tools/Common/JitInterface/ThunkGenerator/ThunkInput.txt @@ -284,7 +284,6 @@ FUNCTIONS size_t findNameOfToken(CORINFO_MODULE_HANDLE moduleHandle,mdToken token, char * szFQName,size_t FQNameCapacity); bool getSystemVAmd64PassStructInRegisterDescriptor(CORINFO_CLASS_HANDLE structHnd, SYSTEMV_AMD64_CORINFO_STRUCT_REG_PASSING_DESCRIPTOR* structPassInRegDescPtr); uint32_t getLoongArch64PassStructInRegisterFlags(CORINFO_CLASS_HANDLE structHnd); - uint32_t getRISCV64PassStructInRegisterFlags(CORINFO_CLASS_HANDLE structHnd); uint32_t getThreadTLSIndex(void **ppIndirection); const void * getInlinedCallFrameVptr(void **ppIndirection); int32_t * getAddrOfCaptureThreadGlobal(void **ppIndirection); diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/ILCompiler.ReadyToRun.csproj b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/ILCompiler.ReadyToRun.csproj index 642b19544133fc..6096d476c806c9 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/ILCompiler.ReadyToRun.csproj +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/ILCompiler.ReadyToRun.csproj @@ -125,7 +125,6 @@ - diff --git a/src/coreclr/tools/aot/ILCompiler.RyuJit/ILCompiler.RyuJit.csproj b/src/coreclr/tools/aot/ILCompiler.RyuJit/ILCompiler.RyuJit.csproj index 4e1c8859136abe..9eeeab38d17abc 100644 --- a/src/coreclr/tools/aot/ILCompiler.RyuJit/ILCompiler.RyuJit.csproj +++ b/src/coreclr/tools/aot/ILCompiler.RyuJit/ILCompiler.RyuJit.csproj @@ -88,9 +88,6 @@ JitInterface\LoongArch64PassStructInRegister.cs - - JitInterface\RISCV64PassStructInRegister.cs - Pgo\TypeSystemEntityOrUnknown.cs diff --git a/src/coreclr/tools/aot/jitinterface/jitinterface_generated.h b/src/coreclr/tools/aot/jitinterface/jitinterface_generated.h index 7b9e92823d94c4..2b416b5fccf09a 100644 --- a/src/coreclr/tools/aot/jitinterface/jitinterface_generated.h +++ b/src/coreclr/tools/aot/jitinterface/jitinterface_generated.h @@ -138,7 +138,6 @@ struct JitInterfaceCallbacks size_t (* findNameOfToken)(void * thisHandle, CorInfoExceptionClass** ppException, CORINFO_MODULE_HANDLE moduleHandle, unsigned int token, char* szFQName, size_t FQNameCapacity); bool (* getSystemVAmd64PassStructInRegisterDescriptor)(void * thisHandle, CorInfoExceptionClass** ppException, CORINFO_CLASS_HANDLE structHnd, SYSTEMV_AMD64_CORINFO_STRUCT_REG_PASSING_DESCRIPTOR* structPassInRegDescPtr); uint32_t (* getLoongArch64PassStructInRegisterFlags)(void * thisHandle, CorInfoExceptionClass** ppException, CORINFO_CLASS_HANDLE structHnd); - uint32_t (* getRISCV64PassStructInRegisterFlags)(void * thisHandle, CorInfoExceptionClass** ppException, CORINFO_CLASS_HANDLE structHnd); uint32_t (* getThreadTLSIndex)(void * thisHandle, CorInfoExceptionClass** ppException, void** ppIndirection); const void* (* getInlinedCallFrameVptr)(void * thisHandle, CorInfoExceptionClass** ppException, void** ppIndirection); int32_t* (* getAddrOfCaptureThreadGlobal)(void * thisHandle, CorInfoExceptionClass** ppException, void** ppIndirection); @@ -1426,15 +1425,6 @@ class JitInterfaceWrapper : public ICorJitInfo return temp; } - virtual uint32_t getRISCV64PassStructInRegisterFlags( - CORINFO_CLASS_HANDLE structHnd) -{ - CorInfoExceptionClass* pException = nullptr; - uint32_t temp = _callbacks->getRISCV64PassStructInRegisterFlags(_thisHandle, &pException, structHnd); - if (pException != nullptr) throw pException; - return temp; -} - virtual uint32_t getThreadTLSIndex( void** ppIndirection) { diff --git a/src/coreclr/tools/superpmi/superpmi-shared/lwmlist.h b/src/coreclr/tools/superpmi/superpmi-shared/lwmlist.h index 72da671ca974dd..e602fba1839517 100644 --- a/src/coreclr/tools/superpmi/superpmi-shared/lwmlist.h +++ b/src/coreclr/tools/superpmi/superpmi-shared/lwmlist.h @@ -124,7 +124,6 @@ LWM(GetSharedCCtorHelper, DWORDLONG, DWORD) LWM(GetStringConfigValue, DWORD, DWORD) LWM(GetSystemVAmd64PassStructInRegisterDescriptor, DWORDLONG, Agnostic_GetSystemVAmd64PassStructInRegisterDescriptor) LWM(GetLoongArch64PassStructInRegisterFlags, DWORDLONG, DWORD) -LWM(GetRISCV64PassStructInRegisterFlags, DWORDLONG, DWORD) LWM(GetTailCallHelpers, Agnostic_GetTailCallHelpers, Agnostic_CORINFO_TAILCALL_HELPERS) LWM(UpdateEntryPointForTailCall, Agnostic_CORINFO_CONST_LOOKUP, Agnostic_CORINFO_CONST_LOOKUP) LWM(GetThreadTLSIndex, DWORD, DLD) diff --git a/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.cpp b/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.cpp index 8646708e0ec6a5..4da0b172f05ba9 100644 --- a/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.cpp +++ b/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.cpp @@ -6118,31 +6118,6 @@ DWORD MethodContext::repGetLoongArch64PassStructInRegisterFlags(CORINFO_CLASS_HA return value; } -void MethodContext::recGetRISCV64PassStructInRegisterFlags(CORINFO_CLASS_HANDLE structHnd, DWORD value) -{ - if (GetRISCV64PassStructInRegisterFlags == nullptr) - GetRISCV64PassStructInRegisterFlags = new LightWeightMap(); - - DWORDLONG key = CastHandle(structHnd); - - GetRISCV64PassStructInRegisterFlags->Add(key, value); - DEBUG_REC(dmpGetRISCV64PassStructInRegisterFlags(key, value)); -} - -void MethodContext::dmpGetRISCV64PassStructInRegisterFlags(DWORDLONG key, DWORD value) -{ - printf("GetRISCV64PassStructInRegisterFlags key %016" PRIX64 " value-%08X", key, value); -} - -DWORD MethodContext::repGetRISCV64PassStructInRegisterFlags(CORINFO_CLASS_HANDLE structHnd) -{ - DWORDLONG key = CastHandle(structHnd); - - DWORD value = LookupByKeyOrMissNoMessage(GetRISCV64PassStructInRegisterFlags, key); - DEBUG_REP(dmpGetRISCV64PassStructInRegisterFlags(key, value)); - return value; -} - void MethodContext::recGetRelocTypeHint(void* target, WORD result) { if (GetRelocTypeHint == nullptr) diff --git a/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.h b/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.h index bda891866ed27a..920bdbe7a2e65f 100644 --- a/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.h +++ b/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.h @@ -787,10 +787,6 @@ class MethodContext void dmpGetLoongArch64PassStructInRegisterFlags(DWORDLONG key, DWORD value); DWORD repGetLoongArch64PassStructInRegisterFlags(CORINFO_CLASS_HANDLE structHnd); - void recGetRISCV64PassStructInRegisterFlags(CORINFO_CLASS_HANDLE structHnd, DWORD value); - void dmpGetRISCV64PassStructInRegisterFlags(DWORDLONG key, DWORD value); - DWORD repGetRISCV64PassStructInRegisterFlags(CORINFO_CLASS_HANDLE structHnd); - void recGetRelocTypeHint(void* target, WORD result); void dmpGetRelocTypeHint(DWORDLONG key, DWORD value); WORD repGetRelocTypeHint(void* target); @@ -1156,17 +1152,16 @@ enum mcPackets Packet_IsIntrinsic = 192, Packet_UpdateEntryPointForTailCall = 193, Packet_GetLoongArch64PassStructInRegisterFlags = 194, - Packet_GetRISCV64PassStructInRegisterFlags = 195, - Packet_GetExactClasses = 196, - Packet_GetRuntimeTypePointer = 197, - Packet_PrintObjectDescription = 198, - Packet_GetReadonlyStaticFieldValue = 199, - Packet_GetObjectType = 200, - Packet_IsObjectImmutable = 201, - Packet_ExpandRawHandleIntrinsic = 202, - Packet_GetArrayOrStringLength = 203, - Packet_IsEnum = 204, - Packet_GetStringChar = 205, + Packet_GetExactClasses = 195, + Packet_GetRuntimeTypePointer = 196, + Packet_PrintObjectDescription = 197, + Packet_GetReadonlyStaticFieldValue = 198, + Packet_GetObjectType = 199, + Packet_IsObjectImmutable = 200, + Packet_ExpandRawHandleIntrinsic = 201, + Packet_GetArrayOrStringLength = 202, + Packet_IsEnum = 203, + Packet_GetStringChar = 204, }; void SetDebugDumpVariables(); diff --git a/src/coreclr/tools/superpmi/superpmi-shim-collector/icorjitinfo.cpp b/src/coreclr/tools/superpmi/superpmi-shim-collector/icorjitinfo.cpp index 1119921b664b45..02f70a5ed9861e 100644 --- a/src/coreclr/tools/superpmi/superpmi-shim-collector/icorjitinfo.cpp +++ b/src/coreclr/tools/superpmi/superpmi-shim-collector/icorjitinfo.cpp @@ -1454,14 +1454,6 @@ uint32_t interceptor_ICJI::getLoongArch64PassStructInRegisterFlags(CORINFO_CLASS return temp; } -uint32_t interceptor_ICJI::getRISCV64PassStructInRegisterFlags(CORINFO_CLASS_HANDLE structHnd) -{ - mc->cr->AddCall("getRISCV64PassStructInRegisterFlags"); - uint32_t temp = original_ICorJitInfo->getRISCV64PassStructInRegisterFlags(structHnd); - mc->recGetRISCV64PassStructInRegisterFlags(structHnd, temp); - return temp; -} - // Stuff on ICorDynamicInfo uint32_t interceptor_ICJI::getThreadTLSIndex(void** ppIndirection) { diff --git a/src/coreclr/tools/superpmi/superpmi-shim-counter/icorjitinfo_generated.cpp b/src/coreclr/tools/superpmi/superpmi-shim-counter/icorjitinfo_generated.cpp index 20bc281a581759..9a30ebd097346d 100644 --- a/src/coreclr/tools/superpmi/superpmi-shim-counter/icorjitinfo_generated.cpp +++ b/src/coreclr/tools/superpmi/superpmi-shim-counter/icorjitinfo_generated.cpp @@ -1023,13 +1023,6 @@ uint32_t interceptor_ICJI::getLoongArch64PassStructInRegisterFlags( return original_ICorJitInfo->getLoongArch64PassStructInRegisterFlags(structHnd); } -uint32_t interceptor_ICJI::getRISCV64PassStructInRegisterFlags( - CORINFO_CLASS_HANDLE structHnd) -{ - mcs->AddCall("getRISCV64PassStructInRegisterFlags"); - return original_ICorJitInfo->getRISCV64PassStructInRegisterFlags(structHnd); -} - uint32_t interceptor_ICJI::getThreadTLSIndex( void** ppIndirection) { diff --git a/src/coreclr/tools/superpmi/superpmi-shim-simple/icorjitinfo_generated.cpp b/src/coreclr/tools/superpmi/superpmi-shim-simple/icorjitinfo_generated.cpp index cb54b4f75d2797..5c81284fc94105 100644 --- a/src/coreclr/tools/superpmi/superpmi-shim-simple/icorjitinfo_generated.cpp +++ b/src/coreclr/tools/superpmi/superpmi-shim-simple/icorjitinfo_generated.cpp @@ -896,12 +896,6 @@ uint32_t interceptor_ICJI::getLoongArch64PassStructInRegisterFlags( return original_ICorJitInfo->getLoongArch64PassStructInRegisterFlags(structHnd); } -uint32_t interceptor_ICJI::getRISCV64PassStructInRegisterFlags( - CORINFO_CLASS_HANDLE structHnd) -{ - return original_ICorJitInfo->getRISCV64PassStructInRegisterFlags(structHnd); -} - uint32_t interceptor_ICJI::getThreadTLSIndex( void** ppIndirection) { diff --git a/src/coreclr/tools/superpmi/superpmi/icorjitinfo.cpp b/src/coreclr/tools/superpmi/superpmi/icorjitinfo.cpp index 43d2fda34eed9e..c627a042d4f354 100644 --- a/src/coreclr/tools/superpmi/superpmi/icorjitinfo.cpp +++ b/src/coreclr/tools/superpmi/superpmi/icorjitinfo.cpp @@ -1272,12 +1272,6 @@ uint32_t MyICJI::getLoongArch64PassStructInRegisterFlags(CORINFO_CLASS_HANDLE st return jitInstance->mc->repGetLoongArch64PassStructInRegisterFlags(structHnd); } -uint32_t MyICJI::getRISCV64PassStructInRegisterFlags(CORINFO_CLASS_HANDLE structHnd) -{ - jitInstance->mc->cr->AddCall("getRISCV64PassStructInRegisterFlags"); - return jitInstance->mc->repGetRISCV64PassStructInRegisterFlags(structHnd); -} - // Stuff on ICorDynamicInfo uint32_t MyICJI::getThreadTLSIndex(void** ppIndirection) { diff --git a/src/coreclr/vm/jitinterface.cpp b/src/coreclr/vm/jitinterface.cpp index 3dd3edd6c3807e..9533a927e3f3ef 100644 --- a/src/coreclr/vm/jitinterface.cpp +++ b/src/coreclr/vm/jitinterface.cpp @@ -9439,28 +9439,9 @@ uint32_t CEEInfo::getLoongArch64PassStructInRegisterFlags(CORINFO_CLASS_HANDLE c #if defined(TARGET_LOONGARCH64) size = (uint32_t)MethodTable::GetLoongArch64PassStructInRegisterFlags(cls); -#endif - - EE_TO_JIT_TRANSITION_LEAF(); - - return size; -} - -uint32_t CEEInfo::getRISCV64PassStructInRegisterFlags(CORINFO_CLASS_HANDLE cls) -{ - CONTRACTL { - NOTHROW; - GC_NOTRIGGER; - MODE_PREEMPTIVE; - } CONTRACTL_END; - - JIT_TO_EE_TRANSITION_LEAF(); - - uint32_t size = STRUCT_NO_FLOAT_FIELD; - -#if defined(TARGET_RISCV64) +#elif defined(TARGET_RISCV64) size = (uint32_t)MethodTable::GetRiscv64PassStructInRegisterFlags(cls); -#endif // TARGET_RISCV64 +#endif EE_TO_JIT_TRANSITION_LEAF(); From 1a569aeebc404c1007d41bea4f773ca6b589dfcd Mon Sep 17 00:00:00 2001 From: Dong-Heon Jung Date: Fri, 7 Apr 2023 10:51:03 +0900 Subject: [PATCH 12/16] [VM] Restore getLoongArch64PassStructInRegisterFlags In coreclr-jit patch, it makes getRISCV64PassStructInRegisterFlags for RISCV64. So restore getLoongArch64PassStructInRegisterFlags --- src/coreclr/vm/jitinterface.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/coreclr/vm/jitinterface.cpp b/src/coreclr/vm/jitinterface.cpp index 9533a927e3f3ef..767cc1abf70ec1 100644 --- a/src/coreclr/vm/jitinterface.cpp +++ b/src/coreclr/vm/jitinterface.cpp @@ -9439,8 +9439,6 @@ uint32_t CEEInfo::getLoongArch64PassStructInRegisterFlags(CORINFO_CLASS_HANDLE c #if defined(TARGET_LOONGARCH64) size = (uint32_t)MethodTable::GetLoongArch64PassStructInRegisterFlags(cls); -#elif defined(TARGET_RISCV64) - size = (uint32_t)MethodTable::GetRiscv64PassStructInRegisterFlags(cls); #endif EE_TO_JIT_TRANSITION_LEAF(); From d877ee7dbddfef7d3a512cd38203630b7c34c033 Mon Sep 17 00:00:00 2001 From: Dong-Heon Jung Date: Tue, 14 Mar 2023 19:49:57 +0900 Subject: [PATCH 13/16] [VM] FIX TEST ERRORS --- src/coreclr/vm/exceptionhandling.cpp | 42 ++++++++++++++++++++-------- 1 file changed, 31 insertions(+), 11 deletions(-) diff --git a/src/coreclr/vm/exceptionhandling.cpp b/src/coreclr/vm/exceptionhandling.cpp index dfa56629afdf0c..9e6ccc9c9440b7 100644 --- a/src/coreclr/vm/exceptionhandling.cpp +++ b/src/coreclr/vm/exceptionhandling.cpp @@ -22,12 +22,12 @@ #define USE_CURRENT_CONTEXT_IN_FILTER #endif // TARGET_X86 -#if defined(TARGET_ARM) || defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64) +#if defined(TARGET_ARM) || defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64) // ARM/ARM64 uses Caller-SP to locate PSPSym in the funclet frame. #define USE_CALLER_SP_IN_FUNCLET -#endif // TARGET_ARM || TARGET_ARM64 || TARGET_LOONGARCH64 +#endif // TARGET_ARM || TARGET_ARM64 || TARGET_LOONGARCH64 || TARGET_RISCV64 -#if defined(TARGET_ARM) || defined(TARGET_ARM64) || defined(TARGET_X86) || defined(TARGET_LOONGARCH64) +#if defined(TARGET_ARM) || defined(TARGET_ARM64) || defined(TARGET_X86) || defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64) #define ADJUST_PC_UNWOUND_TO_CALL #define STACK_RANGE_BOUNDS_ARE_CALLER_SP #define USE_FUNCLET_CALL_HELPER @@ -36,7 +36,7 @@ // // For x86/Linux, RtlVirtualUnwind sets EstablisherFrame as Caller-SP. #define ESTABLISHER_FRAME_ADDRESS_IS_CALLER_SP -#endif // TARGET_ARM || TARGET_ARM64 || TARGET_X86 || TARGET_LOONGARCH64 +#endif // TARGET_ARM || TARGET_ARM64 || TARGET_X86 || TARGET_LOONGARCH64 || TARGET_RISCV64 #ifndef TARGET_UNIX void NOINLINE @@ -539,6 +539,21 @@ void ExceptionTracker::UpdateNonvolatileRegisters(CONTEXT *pContextRecord, REGDI UPDATEREG(S8); UPDATEREG(Fp); +#elif defined(TARGET_RISCV64) + + UPDATEREG(S1); + UPDATEREG(S2); + UPDATEREG(S3); + UPDATEREG(S4); + UPDATEREG(S5); + UPDATEREG(S6); + UPDATEREG(S7); + UPDATEREG(S8); + UPDATEREG(S9); + UPDATEREG(S10); + UPDATEREG(S11); + UPDATEREG(Fp); + #else PORTABILITY_ASSERT("ExceptionTracker::UpdateNonvolatileRegisters"); #endif @@ -823,7 +838,7 @@ UINT_PTR ExceptionTracker::FinishSecondPass( // On ARM & ARM64, we save off the original PC in Lr. This is the same as done // in HandleManagedFault for H/W generated exceptions. pContextRecord->Lr = uResumePC; -#elif defined(TARGET_LOONGARCH64) +#elif defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64) pContextRecord->Ra = uResumePC; #endif @@ -1473,10 +1488,10 @@ void ExceptionTracker::InitializeCrawlFrame(CrawlFrame* pcfThisFrame, Thread* pT } else { -#if defined(TARGET_ARM) || defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64) +#if defined(TARGET_ARM) || defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64) // See the comment above the call to InitRegDisplay for this assertion. _ASSERTE(pDispatcherContext->ControlPc == GetIP(pDispatcherContext->ContextRecord)); -#endif // TARGET_ARM || TARGET_ARM64 || TARGET_LOONGARCH64 +#endif // TARGET_ARM || TARGET_ARM64 || TARGET_LOONGARCH64 || TARGET_RISCV64 #ifdef ESTABLISHER_FRAME_ADDRESS_IS_CALLER_SP // Simply setup the callerSP during the second pass in the caller context. @@ -3272,6 +3287,8 @@ static inline UINT_PTR *GetFirstNonVolatileRegisterAddress(PCONTEXT pContextReco return (UINT_PTR*)&(pContextRecord->S0); #elif defined(TARGET_X86) return (UINT_PTR*)&(pContextRecord->Edi); +#elif defined(TARGET_RISCV64) + return (UINT_PTR*)&(pContextRecord->S1); #else PORTABILITY_ASSERT("GetFirstNonVolatileRegisterAddress"); return NULL; @@ -3280,7 +3297,7 @@ static inline UINT_PTR *GetFirstNonVolatileRegisterAddress(PCONTEXT pContextReco static inline TADDR GetFrameRestoreBase(PCONTEXT pContextRecord) { -#if defined(TARGET_ARM) || defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64) +#if defined(TARGET_ARM) || defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64) return GetSP(pContextRecord); #elif defined(TARGET_X86) return pContextRecord->Ebp; @@ -5314,7 +5331,7 @@ BOOL HandleHardwareException(PAL_SEHException* ex) Thread *pThread = GetThreadNULLOk(); if (pThread != NULL && g_pDebugInterface != NULL) { -#if (defined(TARGET_ARM) || defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64)) +#if (defined(TARGET_ARM) || defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64)) || defined(TARGET_RISCV64) // On ARM and ARM64 and LOONGARCH64 exception point to the break instruction. // See https://static.docs.arm.com/ddi0487/db/DDI0487D_b_armv8_arm.pdf#page=6916&zoom=100,0,152 // at aarch64/exceptions/debug/AArch64.SoftwareBreakpoint @@ -5521,7 +5538,7 @@ void FixupDispatcherContext(DISPATCHER_CONTEXT* pDispatcherContext, CONTEXT* pCo pDispatcherContext->ControlPc = (UINT_PTR) GetIP(pDispatcherContext->ContextRecord); -#if defined(TARGET_ARM) || defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64) +#if defined(TARGET_ARM) || defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64) // Since this routine is used to fixup contexts for async exceptions, // clear the CONTEXT_UNWOUND_TO_CALL flag since, semantically, frames // where such exceptions have happened do not have callsites. On a similar @@ -5547,12 +5564,15 @@ void FixupDispatcherContext(DISPATCHER_CONTEXT* pDispatcherContext, CONTEXT* pCo #elif defined(TARGET_LOONGARCH64) // But keep the architecture flag set (its part of CONTEXT_DEBUG_REGISTERS) pDispatcherContext->ContextRecord->ContextFlags |= CONTEXT_LOONGARCH64; +#elif defined(TARGET_RISCV64) + // But keep the architecture flag set (its part of CONTEXT_DEBUG_REGISTERS) + pDispatcherContext->ContextRecord->ContextFlags |= CONTEXT_RISCV64; #else // TARGET_ARM64 // But keep the architecture flag set (its part of CONTEXT_DEBUG_REGISTERS) pDispatcherContext->ContextRecord->ContextFlags |= CONTEXT_ARM64; #endif // TARGET_ARM -#endif // TARGET_ARM || TARGET_ARM64 || TARGET_LOONGARCH64 +#endif // TARGET_ARM || TARGET_ARM64 || TARGET_LOONGARCH64 || TARGET_RISCV64 INDEBUG(pDispatcherContext->FunctionEntry = (PT_RUNTIME_FUNCTION)INVALID_POINTER_CD); INDEBUG(pDispatcherContext->ImageBase = INVALID_POINTER_CD); From a0845cd89b8959ed0347d930d56c6b223ddd3cf6 Mon Sep 17 00:00:00 2001 From: Dong-Heon Jung Date: Fri, 17 Mar 2023 10:52:01 +0900 Subject: [PATCH 14/16] [VM] Update Stubs --- src/coreclr/vm/riscv64/stubs.cpp | 101 ++++++++++++++++++++++++++++++- 1 file changed, 98 insertions(+), 3 deletions(-) diff --git a/src/coreclr/vm/riscv64/stubs.cpp b/src/coreclr/vm/riscv64/stubs.cpp index 2904f3a22490ea..3e7483e51d7430 100644 --- a/src/coreclr/vm/riscv64/stubs.cpp +++ b/src/coreclr/vm/riscv64/stubs.cpp @@ -639,20 +639,115 @@ void InlinedCallFrame::UpdateRegDisplay(const PREGDISPLAY pRD) #ifdef FEATURE_HIJACK TADDR ResumableFrame::GetReturnAddressPtr(void) { - _ASSERTE(!"RISCV64: not implementation on riscv64!!!"); LIMITED_METHOD_DAC_CONTRACT; return dac_cast(m_Regs) + offsetof(T_CONTEXT, Pc); } void ResumableFrame::UpdateRegDisplay(const PREGDISPLAY pRD) { - _ASSERTE(!"RISCV64: not implementation on riscv64!!!"); + CONTRACT_VOID + { + NOTHROW; + GC_NOTRIGGER; + MODE_ANY; + SUPPORTS_DAC; + } + CONTRACT_END; + + CopyMemory(pRD->pCurrentContext, m_Regs, sizeof(T_CONTEXT)); + + pRD->ControlPC = m_Regs->Pc; + pRD->SP = m_Regs->Sp; + + pRD->pCurrentContextPointers->S1 = &m_Regs->S1; + pRD->pCurrentContextPointers->S2 = &m_Regs->S2; + pRD->pCurrentContextPointers->S3 = &m_Regs->S3; + pRD->pCurrentContextPointers->S4 = &m_Regs->S4; + pRD->pCurrentContextPointers->S5 = &m_Regs->S5; + pRD->pCurrentContextPointers->S6 = &m_Regs->S6; + pRD->pCurrentContextPointers->S7 = &m_Regs->S7; + pRD->pCurrentContextPointers->S8 = &m_Regs->S8; + pRD->pCurrentContextPointers->S9 = &m_Regs->S9; + pRD->pCurrentContextPointers->S10 = &m_Regs->S10; + pRD->pCurrentContextPointers->S11 = &m_Regs->S11; + pRD->pCurrentContextPointers->Tp = &m_Regs->Tp; + pRD->pCurrentContextPointers->Gp = &m_Regs->Gp; + pRD->pCurrentContextPointers->Fp = &m_Regs->Fp; + pRD->pCurrentContextPointers->Ra = &m_Regs->Ra; + + pRD->volatileCurrContextPointers.R0 = &m_Regs->R0; + pRD->volatileCurrContextPointers.A0 = &m_Regs->A0; + pRD->volatileCurrContextPointers.A1 = &m_Regs->A1; + pRD->volatileCurrContextPointers.A2 = &m_Regs->A2; + pRD->volatileCurrContextPointers.A3 = &m_Regs->A3; + pRD->volatileCurrContextPointers.A4 = &m_Regs->A4; + pRD->volatileCurrContextPointers.A5 = &m_Regs->A5; + pRD->volatileCurrContextPointers.A6 = &m_Regs->A6; + pRD->volatileCurrContextPointers.A7 = &m_Regs->A7; + pRD->volatileCurrContextPointers.T0 = &m_Regs->T0; + pRD->volatileCurrContextPointers.T1 = &m_Regs->T1; + pRD->volatileCurrContextPointers.T2 = &m_Regs->T2; + pRD->volatileCurrContextPointers.T3 = &m_Regs->T3; + pRD->volatileCurrContextPointers.T4 = &m_Regs->T4; + pRD->volatileCurrContextPointers.T5 = &m_Regs->T5; + pRD->volatileCurrContextPointers.T6 = &m_Regs->T6; + + pRD->IsCallerContextValid = FALSE; + pRD->IsCallerSPValid = FALSE; // Don't add usage of this field. This is only temporary. + + LOG((LF_GCROOTS, LL_INFO100000, "STACKWALK ResumableFrame::UpdateRegDisplay(pc:%p, sp:%p)\n", pRD->ControlPC, pRD->SP)); + RETURN; } void HijackFrame::UpdateRegDisplay(const PREGDISPLAY pRD) { - _ASSERTE(!"RISCV64: not implementation on riscv64!!!"); + LIMITED_METHOD_CONTRACT; + + pRD->IsCallerContextValid = FALSE; + pRD->IsCallerSPValid = FALSE; + + pRD->pCurrentContext->Pc = m_ReturnAddress; + size_t s = sizeof(struct HijackArgs); + _ASSERTE(s%8 == 0); // HijackArgs contains register values and hence will be a multiple of 8 + // stack must be multiple of 16. So if s is not multiple of 16 then there must be padding of 8 bytes + s = s + s%16; + pRD->pCurrentContext->Sp = PTR_TO_TADDR(m_Args) + s ; + + pRD->pCurrentContext->S1 = m_Args->S1; + pRD->pCurrentContext->S2 = m_Args->S2; + pRD->pCurrentContext->S3 = m_Args->S3; + pRD->pCurrentContext->S4 = m_Args->S4; + pRD->pCurrentContext->S5 = m_Args->S5; + pRD->pCurrentContext->S6 = m_Args->S6; + pRD->pCurrentContext->S7 = m_Args->S7; + pRD->pCurrentContext->S8 = m_Args->S8; + pRD->pCurrentContext->S9 = m_Args->S9; + pRD->pCurrentContext->S10 = m_Args->S10; + pRD->pCurrentContext->S11 = m_Args->S11; + pRD->pCurrentContext->Gp = m_Args->Gp; + pRD->pCurrentContext->Tp = m_Args->Tp; + pRD->pCurrentContext->Fp = m_Args->Fp; + pRD->pCurrentContext->Ra = m_Args->Ra; + + pRD->pCurrentContextPointers->S1 = &m_Args->S1; + pRD->pCurrentContextPointers->S2 = &m_Args->S2; + pRD->pCurrentContextPointers->S3 = &m_Args->S3; + pRD->pCurrentContextPointers->S4 = &m_Args->S4; + pRD->pCurrentContextPointers->S5 = &m_Args->S5; + pRD->pCurrentContextPointers->S6 = &m_Args->S6; + pRD->pCurrentContextPointers->S7 = &m_Args->S7; + pRD->pCurrentContextPointers->S8 = &m_Args->S8; + pRD->pCurrentContextPointers->S9 = &m_Args->S9; + pRD->pCurrentContextPointers->S10 = &m_Args->S10; + pRD->pCurrentContextPointers->S11 = &m_Args->S11; + pRD->pCurrentContextPointers->Gp = &m_Args->Gp; + pRD->pCurrentContextPointers->Tp = &m_Args->Tp; + pRD->pCurrentContextPointers->Fp = &m_Args->Fp; + pRD->pCurrentContextPointers->Ra = NULL; + SyncRegDisplayToCurrentContext(pRD); + + LOG((LF_GCROOTS, LL_INFO100000, "STACKWALK HijackFrame::UpdateRegDisplay(pc:%p, sp:%p)\n", pRD->ControlPC, pRD->SP)); } #endif // FEATURE_HIJACK From aa25adb2a658704bd3558260437010c3a373c04d Mon Sep 17 00:00:00 2001 From: Timur Mustafin Date: Sat, 1 Apr 2023 01:21:22 +0300 Subject: [PATCH 15/16] Fix exceptionhandling, stack smashing detected --- src/coreclr/vm/exceptionhandling.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coreclr/vm/exceptionhandling.cpp b/src/coreclr/vm/exceptionhandling.cpp index 9e6ccc9c9440b7..02c7398a9c9948 100644 --- a/src/coreclr/vm/exceptionhandling.cpp +++ b/src/coreclr/vm/exceptionhandling.cpp @@ -3288,7 +3288,7 @@ static inline UINT_PTR *GetFirstNonVolatileRegisterAddress(PCONTEXT pContextReco #elif defined(TARGET_X86) return (UINT_PTR*)&(pContextRecord->Edi); #elif defined(TARGET_RISCV64) - return (UINT_PTR*)&(pContextRecord->S1); + return (UINT_PTR*)&(pContextRecord->Fp); #else PORTABILITY_ASSERT("GetFirstNonVolatileRegisterAddress"); return NULL; From 1e1d716e3551f4248fcdc5e1546686b7d43e004c Mon Sep 17 00:00:00 2001 From: Dong-Heon Jung Date: Fri, 14 Apr 2023 00:09:32 +0900 Subject: [PATCH 16/16] [VM] Fix vm/riscv64 --- src/coreclr/vm/riscv64/stubs.cpp | 7 +++---- src/coreclr/vm/riscv64/thunktemplates.S | 8 ++++---- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/src/coreclr/vm/riscv64/stubs.cpp b/src/coreclr/vm/riscv64/stubs.cpp index 3e7483e51d7430..4c40d61acb26e2 100644 --- a/src/coreclr/vm/riscv64/stubs.cpp +++ b/src/coreclr/vm/riscv64/stubs.cpp @@ -867,10 +867,9 @@ AdjustContextForVirtualStub( PCODE f_IP = GetIP(pContext); - VirtualCallStubManager::StubKind sk; - VirtualCallStubManager::FindStubManager(f_IP, &sk); + StubCodeBlockKind sk = RangeSectionStubManager::GetStubKind(f_IP); - if (sk == VirtualCallStubManager::SK_DISPATCH) + if (sk == STUB_CODE_BLOCK_VSD_DISPATCH_STUB) { if (*PTR_DWORD(f_IP - 4) != DISPATCH_STUB_FIRST_DWORD) { @@ -879,7 +878,7 @@ AdjustContextForVirtualStub( } } else - if (sk == VirtualCallStubManager::SK_RESOLVE) + if (sk == STUB_CODE_BLOCK_VSD_RESOLVE_STUB) { if (*PTR_DWORD(f_IP) != RESOLVE_STUB_FIRST_DWORD) { diff --git a/src/coreclr/vm/riscv64/thunktemplates.S b/src/coreclr/vm/riscv64/thunktemplates.S index 53d81aab388099..1bc674688daafe 100644 --- a/src/coreclr/vm/riscv64/thunktemplates.S +++ b/src/coreclr/vm/riscv64/thunktemplates.S @@ -5,25 +5,25 @@ #include "asmconstants.h" LEAF_ENTRY StubPrecodeCode - auipc t1, 0x1 + auipc t1, 0x4 ld t2, (StubPrecodeData__MethodDesc)(t1) ld t1, (StubPrecodeData__Target)(t1) jr t1 LEAF_END_MARKED StubPrecodeCode LEAF_ENTRY FixupPrecodeCode - auipc t2, 0x1 + auipc t2, 0x4 ld t2, (FixupPrecodeData__Target)(t2) c.jr t2 - auipc t2, 0x1 + auipc t2, 0x4 ld t1, (FixupPrecodeData__PrecodeFixupThunk - 0xa)(t2) ld t2, (FixupPrecodeData__MethodDesc - 0xa)(t2) jr t1 LEAF_END_MARKED FixupPrecodeCode LEAF_ENTRY CallCountingStubCode - auipc t2, 0x1 + auipc t2, 0x4 ld t3, (CallCountingStubData__RemainingCallCountCell)(t2) lh t1, 0(t3) addiw t1, t1, -1