[LoongArch64] coreclr-vm directory (#62885)
authorQiao Pengcheng <qiaopengcheng@loongson.cn>
Thu, 21 Apr 2022 16:55:30 +0000 (00:55 +0800)
committerGitHub <noreply@github.com>
Thu, 21 Apr 2022 16:55:30 +0000 (09:55 -0700)
* Part6-2: -add the coreclr-vm directory for LoongArch64. (#59561)

Co-authored-by: Loongson's .NET-teams
* [LoongArch64] revert the syncblk.cpp.

* [LoongArch64] delete some unused codes.

* [LoongArch64] add vm/CMakeLists.txt from #62889.

* [LoongArch64] add related files from #62886 and #62893.

* [LoongArch64] moved vm/jitinterface.cpp from #62893.
moved inc/corinfo.h from #6288.

* [LoongArch64] run the file `src/coreclr/tools/Common/JitInterface/ThunkGenerator/gen.sh`
and merge the patch from #62893.

* [LoongArch64] revert vm/ceeload.h.
amend the `ToolBox/superpmi/superpmi-shared/agnostic.h`

* [LoongArch64] add empty interfaces within `CorInfoImpl.cs`.

* [LoongArch64] Fix the compiling error on Windows.

* [LoongArch64] Fix the compiling error for memory unaligned m_currentByteStackIndex.

* [LoongArch64] Delete the !TARGET_LOONGARCH64 for m_currentByteStackIndex.

* [LoongArch64] move ToolBox from #62886.

* [LoongArch64] amend the args when needs unsigned extending within CallTargetWorker.

* [LoongArch64] add bool type for args' unsigned extention.

* [LoongArch64] adding char type for args' unsigned extention.
Also rename `TARGET_LOONGARCH64` to `UNIX_LOONGARCH64_ABI` within ABI.

* [LoongArch64] amend renaming `TARGET_LOONGARCH64` to `UNIX_LOONGARCH64_ABI` within ABI.

* [LoongArch64] remove the JIT/EE interface to #62893.

* [LoongArch64] revert the rename `TARGET_LOONGARCH64` to `UNIX_LOONGARCH64_ABI`.
Also add unsigned extend for CHAR type.

* [LoongArch64] refactor the `ArgDestination and ArgLocDesc`.

* [LoongArch64] rename the `m_flag` and `getFieldTypeByHnd`.

* [LoongArch64] add `NATIVE_SYMBOL_READER_DLL` for compiling error.

* [LoongArch64] update the version of the `LICENSE description`.

* [LoongArch64] keep same with the comment in `jit/targetloongarch64.h`

* [LoongArch64] amend the code for reviewing.

* [LoongArch64] refactor LoongArch64-ABI within `vm`,
and also amend some code for review.

* [LoongArch64] delete unused codes.

* [LoongArch64] merge main for #65869.

* [LoongArch64] amend the format for reviewing.

* [LoongArch64] delete some unused code for reviewing.

* [LoongArch64] amend code for CR feedback @jkotas @janvorli

* [LoongArch64] add class type for LoongArch64-ABI.

* [LoongArch64] Amend the LoongArch64's ABI
after merged `MethodTable::GetLoongArch64PassStructInRegisterFlags()`
and `CEEInfo::getLoongArch64PassStructInRegisterFlags()`

* [LoongArch64] Fix the assert error
when running hello-world within debug-mode after refacting.

Co-authored-by: qiaopengcheng <qiaopengcheng-hf@loongson.cn>
57 files changed:
src/coreclr/inc/clrnt.h
src/coreclr/inc/eetwain.h
src/coreclr/inc/jithelpers.h
src/coreclr/inc/stdmacros.h
src/coreclr/tools/superpmi/superpmi-shared/compileresult.cpp
src/coreclr/tools/superpmi/superpmi-shared/spmiutil.cpp
src/coreclr/tools/superpmi/superpmi-shared/spmiutil.h
src/coreclr/tools/superpmi/superpmi/superpmi.cpp
src/coreclr/vm/CMakeLists.txt
src/coreclr/vm/argdestination.h
src/coreclr/vm/callcounting.h
src/coreclr/vm/callhelpers.cpp
src/coreclr/vm/callingconvention.h
src/coreclr/vm/ceeload.h
src/coreclr/vm/cgensys.h
src/coreclr/vm/codeman.cpp
src/coreclr/vm/codeman.h
src/coreclr/vm/comdelegate.cpp
src/coreclr/vm/dynamicmethod.cpp
src/coreclr/vm/eetwain.cpp
src/coreclr/vm/encee.cpp
src/coreclr/vm/excep.cpp
src/coreclr/vm/exceptionhandling.cpp
src/coreclr/vm/frames.h
src/coreclr/vm/gccover.cpp
src/coreclr/vm/gccover.h
src/coreclr/vm/gcenv.ee.cpp
src/coreclr/vm/gcinfodecoder.cpp
src/coreclr/vm/gdbjit.cpp
src/coreclr/vm/interpreter.cpp
src/coreclr/vm/interpreter.h
src/coreclr/vm/jitinterface.cpp
src/coreclr/vm/jitinterface.h
src/coreclr/vm/loongarch64/asmconstants.h [new file with mode: 0644]
src/coreclr/vm/loongarch64/asmhelpers.S [new file with mode: 0644]
src/coreclr/vm/loongarch64/calldescrworkerloongarch64.S [new file with mode: 0644]
src/coreclr/vm/loongarch64/cgencpu.h [new file with mode: 0644]
src/coreclr/vm/loongarch64/crthelpers.S [new file with mode: 0644]
src/coreclr/vm/loongarch64/excepcpu.h [new file with mode: 0644]
src/coreclr/vm/loongarch64/gmscpu.h [new file with mode: 0644]
src/coreclr/vm/loongarch64/pinvokestubs.S [new file with mode: 0644]
src/coreclr/vm/loongarch64/profiler.cpp [new file with mode: 0644]
src/coreclr/vm/loongarch64/stubs.cpp [new file with mode: 0644]
src/coreclr/vm/loongarch64/thunktemplates.S [new file with mode: 0644]
src/coreclr/vm/loongarch64/unixstubs.cpp [new file with mode: 0644]
src/coreclr/vm/loongarch64/virtualcallstubcpu.hpp [new file with mode: 0644]
src/coreclr/vm/methodtable.cpp
src/coreclr/vm/methodtable.h
src/coreclr/vm/object.cpp
src/coreclr/vm/precode.cpp
src/coreclr/vm/precode.h
src/coreclr/vm/prestub.cpp
src/coreclr/vm/stackwalk.cpp
src/coreclr/vm/stackwalk.h
src/coreclr/vm/stubmgr.cpp
src/coreclr/vm/threads.cpp
src/coreclr/vm/threadsuspend.cpp

index 0e1480c..0c87336 100644 (file)
@@ -1069,14 +1069,6 @@ RtlVirtualUnwind(
     IN OUT PKNONVOLATILE_CONTEXT_POINTERS ContextPointers OPTIONAL
     );
 
-#ifndef IMAGE_REL_LOONGARCH64_PC
-#define IMAGE_REL_LOONGARCH64_PC  0x0003
-#endif
-
-#ifndef IMAGE_REL_LOONGARCH64_JIR
-#define IMAGE_REL_LOONGARCH64_JIR  0x0004
-#endif
-
 #endif // TARGET_LOONGARCH64
 
 #endif  // CLRNT_H_
index f2c51b5..8653d5e 100644 (file)
@@ -211,9 +211,9 @@ virtual bool UnwindStackFrame(PREGDISPLAY     pContext,
 virtual bool IsGcSafe(EECodeInfo     *pCodeInfo,
                       DWORD           dwRelOffset) = 0;
 
-#if defined(TARGET_ARM) || defined(TARGET_ARM64)
+#if defined(TARGET_ARM) || defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64)
 virtual bool HasTailCalls(EECodeInfo *pCodeInfo) = 0;
-#endif // TARGET_ARM || TARGET_ARM64
+#endif // TARGET_ARM || TARGET_ARM64 || TARGET_LOONGARCH64
 
 #if defined(TARGET_AMD64) && defined(_DEBUG)
 /*
@@ -455,10 +455,10 @@ virtual
 bool IsGcSafe(  EECodeInfo     *pCodeInfo,
                 DWORD           dwRelOffset);
 
-#if defined(TARGET_ARM) || defined(TARGET_ARM64)
+#if defined(TARGET_ARM) || defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64)
 virtual
 bool HasTailCalls(EECodeInfo *pCodeInfo);
-#endif // TARGET_ARM || TARGET_ARM64
+#endif // TARGET_ARM || TARGET_ARM64 || TARGET_LOONGARCH64
 
 #if defined(TARGET_AMD64) && defined(_DEBUG)
 /*
index e2c4e0e..2ba380c 100644 (file)
 
     JITHELPER(CORINFO_HELP_GVMLOOKUP_FOR_SLOT, NULL, CORINFO_HELP_SIG_NO_ALIGN_STUB)
 
-#ifndef TARGET_ARM64
+#if !defined(TARGET_ARM64) && !defined(TARGET_LOONGARCH64)
     JITHELPER(CORINFO_HELP_STACK_PROBE, JIT_StackProbe, CORINFO_HELP_SIG_REG_ONLY)
 #else
     JITHELPER(CORINFO_HELP_STACK_PROBE, NULL, CORINFO_HELP_SIG_UNDEF)
index 2d0a057..b6b5030 100644 (file)
 #define NOT_ARM64_ARG(x)    , x
 #endif
 
+#ifdef TARGET_LOONGARCH64
+#define LOONGARCH64_FIRST_ARG(x)  x ,
+#define LOONGARCH64_ARG(x)        , x
+#define LOONGARCH64_ONLY(x)       x
+#define NOT_LOONGARCH64(x)
+#define NOT_LOONGARCH64_ARG(x)
+#else
+#define LOONGARCH64_FIRST_ARG(x)
+#define LOONGARCH64_ARG(x)
+#define LOONGARCH64_ONLY(x)
+#define NOT_LOONGARCH64(x)        x
+#define NOT_LOONGARCH64_ARG(x)    , x
+#endif
+
 #ifdef TARGET_64BIT
 #define LOG2_PTRSIZE 3
 #else
index 65970a0..fdfc772 100644 (file)
@@ -862,6 +862,11 @@ void CompileResult::applyRelocs(RelocContext* rc, unsigned char* block1, ULONG b
             }
         }
 
+        if (targetArch == SPMI_TARGET_ARCHITECTURE_LOONGARCH64)
+        {
+            Assert(!"FIXME: Not Implements on loongarch64");
+        }
+
         if (IsSpmiTarget64Bit())
         {
             if (relocType == IMAGE_REL_BASED_DIR64)
index 4d21d7d..97f4ca4 100644 (file)
@@ -251,6 +251,8 @@ static SPMI_TARGET_ARCHITECTURE SpmiTargetArchitecture = SPMI_TARGET_ARCHITECTUR
 static SPMI_TARGET_ARCHITECTURE SpmiTargetArchitecture = SPMI_TARGET_ARCHITECTURE_ARM;
 #elif defined(TARGET_ARM64)
 static SPMI_TARGET_ARCHITECTURE SpmiTargetArchitecture = SPMI_TARGET_ARCHITECTURE_ARM64;
+#elif defined(TARGET_LOONGARCH64)
+static SPMI_TARGET_ARCHITECTURE SpmiTargetArchitecture = SPMI_TARGET_ARCHITECTURE_LOONGARCH64;
 #else
 #error Unsupported architecture
 #endif
index a4863a1..134658b 100644 (file)
@@ -53,7 +53,8 @@ enum SPMI_TARGET_ARCHITECTURE
     SPMI_TARGET_ARCHITECTURE_X86,
     SPMI_TARGET_ARCHITECTURE_AMD64,
     SPMI_TARGET_ARCHITECTURE_ARM64,
-    SPMI_TARGET_ARCHITECTURE_ARM
+    SPMI_TARGET_ARCHITECTURE_ARM,
+    SPMI_TARGET_ARCHITECTURE_LOONGARCH64
 };
 
 SPMI_TARGET_ARCHITECTURE GetSpmiTargetArchitecture();
@@ -61,7 +62,7 @@ void SetSpmiTargetArchitecture(SPMI_TARGET_ARCHITECTURE spmiTargetArchitecture);
 
 inline bool IsSpmiTarget64Bit()
 {
-    return (GetSpmiTargetArchitecture() == SPMI_TARGET_ARCHITECTURE_AMD64) || (GetSpmiTargetArchitecture() == SPMI_TARGET_ARCHITECTURE_ARM64);
+    return (GetSpmiTargetArchitecture() == SPMI_TARGET_ARCHITECTURE_AMD64) || (GetSpmiTargetArchitecture() == SPMI_TARGET_ARCHITECTURE_ARM64) || (GetSpmiTargetArchitecture() == SPMI_TARGET_ARCHITECTURE_LOONGARCH64);
 }
 
 inline size_t SpmiTargetPointerSize()
index e5a7d7f..d7a8d80 100644 (file)
@@ -53,6 +53,10 @@ void SetSuperPmiTargetArchitecture(const char* targetArchitecture)
         {
             SetSpmiTargetArchitecture(SPMI_TARGET_ARCHITECTURE_ARM64);
         }
+        else if (0 == _stricmp(targetArchitecture, "loongarch64"))
+        {
+            SetSpmiTargetArchitecture(SPMI_TARGET_ARCHITECTURE_LOONGARCH64);
+        }
         else
         {
             LogError("Illegal target architecture '%s'", targetArchitecture);
index b9147ba..6a1b5d9 100644 (file)
@@ -762,6 +762,14 @@ else(CLR_CMAKE_TARGET_WIN32)
             ${ARCH_SOURCES_DIR}/pinvokestubs.S
             ${ARCH_SOURCES_DIR}/thunktemplates.S
         )
+    elseif(CLR_CMAKE_TARGET_ARCH_LOONGARCH64)
+        set(VM_SOURCES_WKS_ARCH_ASM
+            ${ARCH_SOURCES_DIR}/asmhelpers.S
+            ${ARCH_SOURCES_DIR}/calldescrworkerloongarch64.S
+            ${ARCH_SOURCES_DIR}/crthelpers.S
+            ${ARCH_SOURCES_DIR}/pinvokestubs.S
+            ${ARCH_SOURCES_DIR}/thunktemplates.S
+        )
     endif()
 
 endif(CLR_CMAKE_TARGET_WIN32)
@@ -864,6 +872,21 @@ elseif(CLR_CMAKE_TARGET_ARCH_ARM64)
             ${ARCH_SOURCES_DIR}/arm64singlestepper.cpp
         )
     endif(CLR_CMAKE_HOST_UNIX)
+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
+    )
 endif()
 
 if(CLR_CMAKE_HOST_UNIX)
index d4db4b6..59f1514 100644 (file)
@@ -29,8 +29,8 @@ public:
         LIMITED_METHOD_CONTRACT;
 #if defined(UNIX_AMD64_ABI)
         _ASSERTE((argLocDescForStructInRegs != NULL) || (offset != TransitionBlock::StructInRegsOffset));
-#elif defined(TARGET_ARM64)
-        // This assert is not interesting on arm64. argLocDescForStructInRegs could be
+#elif defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64)
+        // This assert is not interesting on arm64/loongarch64. argLocDescForStructInRegs could be
         // initialized if the args are being enregistered.
 #else
         _ASSERTE(argLocDescForStructInRegs == NULL);
@@ -83,6 +83,67 @@ public:
 #endif // !DACCESS_COMPILE
 #endif // defined(TARGET_ARM64)
 
+#if defined(TARGET_LOONGARCH64)
+    bool IsStructPassedInRegs()
+    {
+        return m_argLocDescForStructInRegs != NULL;
+    }
+
+#ifndef DACCESS_COMPILE
+    void CopyStructToRegisters(void *src, int fieldBytes)
+    {
+        _ASSERTE(IsStructPassedInRegs());
+        _ASSERTE(fieldBytes <= 16);
+
+        int argOfs = TransitionBlock::GetOffsetOfFloatArgumentRegisters() + m_argLocDescForStructInRegs->m_idxFloatReg * 8;
+
+        if (m_argLocDescForStructInRegs->m_structFields == STRUCT_FLOAT_FIELD_ONLY_TWO)
+        { // struct with two floats.
+            _ASSERTE(m_argLocDescForStructInRegs->m_cFloatReg == 2);
+            _ASSERTE(m_argLocDescForStructInRegs->m_cGenReg == 0);
+            *(INT64*)((char*)m_base + argOfs) = *(INT32*)src;
+            *(INT64*)((char*)m_base + argOfs + 8) = *((INT32*)src + 1);
+        }
+        else if ((m_argLocDescForStructInRegs->m_structFields & STRUCT_FLOAT_FIELD_FIRST) != 0)
+        { // the first field is float or double.
+            if ((m_argLocDescForStructInRegs->m_structFields & STRUCT_FIRST_FIELD_SIZE_IS8) == 0)
+                *(INT64*)((char*)m_base + argOfs) = *(INT32*)src; // the first field is float
+            else
+                *(UINT64*)((char*)m_base + argOfs) = *(UINT64*)src; // the first field is double.
+            _ASSERTE(m_argLocDescForStructInRegs->m_cFloatReg == 1);
+            _ASSERTE(m_argLocDescForStructInRegs->m_cGenReg == 1);
+            _ASSERTE((m_argLocDescForStructInRegs->m_structFields & STRUCT_FLOAT_FIELD_SECOND) == 0);//the second field is integer.
+            argOfs = TransitionBlock::GetOffsetOfArgumentRegisters() + m_argLocDescForStructInRegs->m_idxGenReg * 8;
+            if ((m_argLocDescForStructInRegs->m_structFields & STRUCT_HAS_8BYTES_FIELDS_MASK) != 0)
+                *(UINT64*)((char*)m_base + argOfs) = *((UINT64*)src + 1);
+            else
+                *(INT64*)((char*)m_base + argOfs) = *((INT32*)src + 1); // the second field is int32.
+        }
+        else if ((m_argLocDescForStructInRegs->m_structFields & STRUCT_FLOAT_FIELD_SECOND) != 0)
+        { // the second field is float or double.
+            *(UINT64*)((char*)m_base + argOfs) = *(UINT64*)src; // NOTE: here ignoring the first size.
+            if ((m_argLocDescForStructInRegs->m_structFields & STRUCT_HAS_8BYTES_FIELDS_MASK) == 0)
+                *(UINT64*)((char*)m_base + argOfs) = *((INT32*)src + 1); // the second field is int32.
+            else
+                *(UINT64*)((char*)m_base + argOfs) = *((UINT64*)src + 1);
+            _ASSERTE(m_argLocDescForStructInRegs->m_cFloatReg == 1);
+            _ASSERTE(m_argLocDescForStructInRegs->m_cGenReg == 1);
+            _ASSERTE((m_argLocDescForStructInRegs->m_structFields & STRUCT_FLOAT_FIELD_FIRST) == 0);//the first field is integer.
+            argOfs = TransitionBlock::GetOffsetOfArgumentRegisters() + m_argLocDescForStructInRegs->m_idxGenReg * 8;
+        }
+        else
+            _ASSERTE(!"---------UNReachable-------LoongArch64!!!");
+    }
+#endif // !DACCESS_COMPILE
+
+    PTR_VOID GetStructGenRegDestinationAddress()
+    {
+        _ASSERTE(IsStructPassedInRegs());
+        int argOfs = TransitionBlock::GetOffsetOfArgumentRegisters() + m_argLocDescForStructInRegs->m_idxGenReg * 8;
+        return dac_cast<PTR_VOID>(dac_cast<TADDR>(m_base) + argOfs);
+    }
+#endif // defined(TARGET_LOONGARCH64)
+
 #if defined(UNIX_AMD64_ABI)
 
     // Returns true if the ArgDestination represents a struct passed in registers.
index 089702e..3d25e1c 100644 (file)
@@ -97,6 +97,8 @@ public:
     static const int CodeSize = 40;
 #elif defined(TARGET_ARM)
     static const int CodeSize = 32;
+#elif defined(TARGET_LOONGARCH64)
+    static const int CodeSize = 40;
 #endif
 
 private:
index 711d6d5..7b99261 100644 (file)
@@ -462,17 +462,44 @@ void MethodDescCallSite::CallTargetWorker(const ARG_SLOT *pArguments, ARG_SLOT *
                 argDest.CopyStructToRegisters(pSrc, th.AsMethodTable()->GetNumInstanceFieldBytes(), 0);
             }
             else
-#endif // UNIX_AMD64_ABI
+#elif defined(TARGET_LOONGARCH64)
+            if (argDest.IsStructPassedInRegs())
+            {
+                argDest.CopyStructToRegisters(pSrc, stackSize);
+            }
+            else
+#endif // TARGET_LOONGARCH64
             {
                 PVOID pDest = argDest.GetDestinationAddress();
 
                 switch (stackSize)
                 {
+#if defined(TARGET_LOONGARCH64)
+                    case 1:
+                        if (m_argIt.GetArgType() == ELEMENT_TYPE_U1 || m_argIt.GetArgType() == ELEMENT_TYPE_BOOLEAN)
+                            *((INT64*)pDest) = (UINT8)pArguments[arg];
+                        else
+                            *((INT64*)pDest) = (INT8)pArguments[arg];
+                        break;
+                    case 2:
+                        if (m_argIt.GetArgType() == ELEMENT_TYPE_U2 || m_argIt.GetArgType() == ELEMENT_TYPE_CHAR)
+                            *((INT64*)pDest) = (UINT16)pArguments[arg];
+                        else
+                            *((INT64*)pDest) = (INT16)pArguments[arg];
+                        break;
+                    case 4:
+                        if (m_argIt.GetArgType() == ELEMENT_TYPE_U4)
+                            *((INT64*)pDest) = (UINT32)pArguments[arg];
+                        else
+                            *((INT64*)pDest) = (INT32)pArguments[arg];
+                        break;
+#else
                     case 1:
                     case 2:
                     case 4:
                         *((INT32*)pDest) = (INT32)pArguments[arg];
                         break;
+#endif
 
                     case 8:
                         *((INT64*)pDest) = pArguments[arg];
index 1fe96fd..2e5b1b5 100644 (file)
@@ -41,6 +41,9 @@ struct ArgLocDesc
 
     int     m_byteStackIndex;     // Stack offset in bytes (or -1)
     int     m_byteStackSize;      // Stack size in bytes
+#if defined(TARGET_LOONGARCH64)
+    int     m_structFields;       // Struct field info when using Float-register except two-doubles case.
+#endif
 
 #if defined(UNIX_AMD64_ABI)
 
@@ -93,6 +96,9 @@ struct ArgLocDesc
 #if defined(TARGET_ARM64)
         m_hfaFieldSize = 0;
 #endif // defined(TARGET_ARM64)
+#if defined(TARGET_LOONGARCH64)
+        m_structFields = STRUCT_NO_FLOAT_FIELD;
+#endif
 #if defined(UNIX_AMD64_ABI)
         m_eeClass = NULL;
 #endif
@@ -138,6 +144,26 @@ struct TransitionBlock
     TADDR padding; // Keep size of TransitionBlock as multiple of 16-byte. Simplifies code in PROLOG_WITH_TRANSITION_BLOCK
     INT64 m_x8RetBuffReg;
     ArgumentRegisters       m_argumentRegisters;
+#elif defined(TARGET_LOONGARCH64)
+    union {
+        CalleeSavedRegisters m_calleeSavedRegisters;
+        struct {
+            INT64 fp; // frame pointer
+            TADDR m_ReturnAddress;
+            INT64 s0;
+            INT64 s1;
+            INT64 s2;
+            INT64 s3;
+            INT64 s4;
+            INT64 s5;
+            INT64 s6;
+            INT64 s7;
+            INT64 s8;
+            INT64 tp;
+        };
+    };
+    //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
@@ -476,6 +502,9 @@ public:
 #elif defined(TARGET_ARM64)
         // Composites greater than 16 bytes are passed by reference
         return ((size > ENREGISTERED_PARAMTYPE_MAXSIZE) && !th.IsHFA());
+#elif defined(TARGET_LOONGARCH64)
+        // Composites greater than 16 bytes are passed by reference
+        return (size > ENREGISTERED_PARAMTYPE_MAXSIZE);
 #else
         PORTABILITY_ASSERT("ArgIteratorTemplate::IsArgPassedByRef");
         return FALSE;
@@ -530,6 +559,13 @@ public:
             return ((m_argSize > ENREGISTERED_PARAMTYPE_MAXSIZE) && (!m_argTypeHandle.IsHFA() || this->IsVarArg()));
         }
         return FALSE;
+#elif defined(TARGET_LOONGARCH64)
+        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;
@@ -591,7 +627,7 @@ public:
 
     ArgLocDesc* GetArgLocDescForStructInRegs()
     {
-#if defined(UNIX_AMD64_ABI) || defined (TARGET_ARM64)
+#if defined(UNIX_AMD64_ABI) || defined (TARGET_ARM64) || defined(TARGET_LOONGARCH64)
         return m_hasArgLocDescForStructInRegs ? &m_argLocDescForStructInRegs : NULL;
 #else
         return NULL;
@@ -788,6 +824,58 @@ public:
     }
 #endif // TARGET_AMD64
 
+#ifdef TARGET_LOONGARCH64
+    // 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-LOONGARCH64: 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_LOONGARCH64
+
 protected:
     DWORD               m_dwFlags;              // Cached flags
     int                 m_nSizeOfArgStack;      // Cached value of SizeOfArgStack
@@ -798,10 +886,10 @@ protected:
     CorElementType      m_argType;
     int                 m_argSize;
     TypeHandle          m_argTypeHandle;
-#if (defined(TARGET_AMD64) && defined(UNIX_AMD64_ABI)) || defined(TARGET_ARM64)
+#if (defined(TARGET_AMD64) && defined(UNIX_AMD64_ABI)) || defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64)
     ArgLocDesc          m_argLocDescForStructInRegs;
     bool                m_hasArgLocDescForStructInRegs;
-#endif // (TARGET_AMD64 && UNIX_AMD64_ABI) || TARGET_ARM64
+#endif // (TARGET_AMD64 && UNIX_AMD64_ABI) || TARGET_ARM64 || TARGET_LOONGARCH64
 
     int                 m_ofsStack;           // Current position of the stack iterator, in bytes
 
@@ -829,6 +917,12 @@ protected:
     int             m_idxFPReg;         // Next FP register to be assigned a value
 #endif
 
+#ifdef TARGET_LOONGARCH64
+    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,
@@ -1072,6 +1166,10 @@ int ArgIteratorTemplate<ARGITERATOR_BASE>::GetNextOffset()
         m_ofsStack = 0;
 
         m_idxFPReg = 0;
+#elif defined(TARGET_LOONGARCH64)
+        m_idxGenReg = numRegistersUsed;
+        m_ofsStack = 0;
+        m_idxFPReg = 0;
 #else
         PORTABILITY_ASSERT("ArgIteratorTemplate::GetNextOffset");
 #endif
@@ -1094,7 +1192,7 @@ int ArgIteratorTemplate<ARGITERATOR_BASE>::GetNextOffset()
     m_argSize = argSize;
     m_argTypeHandle = thValueType;
 
-#if defined(UNIX_AMD64_ABI) || defined (TARGET_ARM64)
+#if defined(UNIX_AMD64_ABI) || defined (TARGET_ARM64) || defined (TARGET_LOONGARCH64)
     m_hasArgLocDescForStructInRegs = false;
 #endif
 
@@ -1522,6 +1620,132 @@ int ArgIteratorTemplate<ARGITERATOR_BASE>::GetNextOffset()
     int argOfs = TransitionBlock::GetOffsetOfArgs() + m_ofsStack;
     m_ofsStack += cbArg;
     return argOfs;
+#elif defined(TARGET_LOONGARCH64)
+
+    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::GetLoongArch64PassStructInRegisterFlags((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;
+        }
+        else
+            m_idxFPReg = NUM_ARGUMENT_REGISTERS;
+    }
+
+    {
+        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");
     return TransitionBlock::InvalidOffset;
@@ -1557,15 +1781,23 @@ void ArgIteratorTemplate<ARGITERATOR_BASE>::ComputeReturnFlags()
         break;
 
     case ELEMENT_TYPE_R4:
+#if defined(TARGET_LOONGARCH64)
+        flags |= STRUCT_FLOAT_FIELD_ONLY_ONE << RETURN_FP_SIZE_SHIFT;
+#else
 #ifndef ARM_SOFTFP
         flags |= sizeof(float) << RETURN_FP_SIZE_SHIFT;
 #endif
+#endif
         break;
 
     case ELEMENT_TYPE_R8:
+#if defined(TARGET_LOONGARCH64)
+        flags |= (STRUCT_FLOAT_FIELD_ONLY_ONE | STRUCT_FIRST_FIELD_SIZE_IS8) << RETURN_FP_SIZE_SHIFT;
+#else
 #ifndef ARM_SOFTFP
         flags |= sizeof(double) << RETURN_FP_SIZE_SHIFT;
 #endif
+#endif
         break;
 
     case ELEMENT_TYPE_VALUETYPE:
@@ -1629,8 +1861,19 @@ void ArgIteratorTemplate<ARGITERATOR_BASE>::ComputeReturnFlags()
             }
 #endif // defined(TARGET_X86) || defined(TARGET_AMD64)
 
+#if defined(TARGET_LOONGARCH64)
             if  (size <= ENREGISTERED_RETURNTYPE_INTEGER_MAXSIZE)
+            {
+                assert(!thValueType.IsTypeDesc());
+
+                MethodTable *pMethodTable = thValueType.AsMethodTable();
+                flags = (MethodTable::GetLoongArch64PassStructInRegisterFlags((CORINFO_CLASS_HANDLE)pMethodTable) & 0xff) << RETURN_FP_SIZE_SHIFT;
                 break;
+            }
+#else
+            if  (size <= ENREGISTERED_RETURNTYPE_INTEGER_MAXSIZE)
+                break;
+#endif
 #endif // UNIX_AMD64_ABI
         }
 #endif // ENREGISTERED_RETURNTYPE_INTEGER_MAXSIZE
index 9c2adf3..a364179 100644 (file)
@@ -87,6 +87,8 @@ class JITInlineTrackingMap;
 #define NATIVE_SYMBOL_READER_DLL W("Microsoft.DiaSymReader.Native.arm.dll")
 #elif defined(HOST_ARM64)
 #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")
 #endif
 
 typedef DPTR(JITInlineTrackingMap) PTR_JITInlineTrackingMap;
index f66614a..868e9cf 100644 (file)
@@ -58,7 +58,6 @@ extern "C" void STDCALL GenericPInvokeCalliStubWorker(TransitionBlock * pTransit
 extern "C" void STDCALL GenericPInvokeCalliHelper(void);
 
 extern "C" PCODE STDCALL ExternalMethodFixupWorker(TransitionBlock * pTransitionBlock, TADDR pIndirection, DWORD sectionIndex, Module * pModule);
-extern "C" void STDCALL ExternalMethodFixupStub(void);
 extern "C" void STDCALL ExternalMethodFixupPatchLabel(void);
 
 extern "C" void STDCALL VirtualMethodFixupStub(void);
index 6f7cf19..c5b296c 100644 (file)
@@ -812,7 +812,7 @@ ExecutionManager::DeleteRangeHelper
 
 //-----------------------------------------------------------------------------
 
-#if defined(TARGET_ARM) || defined(TARGET_ARM64)
+#if defined(TARGET_ARM) || defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64)
 #define EXCEPTION_DATA_SUPPORTS_FUNCTION_FRAGMENTS
 #endif
 
@@ -882,6 +882,41 @@ BOOL IsFunctionFragment(TADDR baseAddress, PTR_RUNTIME_FUNCTION pFunctionEntry)
     }
 
     return ((*pUnwindCodes & 0xFF) == 0xE5);
+#elif defined(TARGET_LOONGARCH64)
+
+    // 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
+    // scenarios which is not supported, hence we don't need to worry about them. discarding partial prologs
+    // simplifies identifying a host record a lot.
+    //
+    // 1. Prolog only: The host record. Epilog Count and E bit are all 0.
+    // 2. Prolog and some epilogs: The host record with accompanying epilog-only records
+    // 3. Epilogs only: First unwind code is Phantom prolog (Starting with an end_c, indicating an empty prolog)
+    // 4. No prologs or epilogs: First unwind code is Phantom prolog  (Starting with an end_c, indicating an empty prolog)
+    //
+
+    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");
 #endif
@@ -1045,6 +1080,47 @@ PTR_VOID GetUnwindDataBlob(TADDR moduleBase, PTR_RUNTIME_FUNCTION pRuntimeFuncti
     *pSize = size;
     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<PTR_ULONG>(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");
     return NULL;
@@ -1628,6 +1704,10 @@ void EEJitManager::SetCpuInfo()
     }
 #endif
 
+#if defined(TARGET_LOONGARCH64)
+    // TODO-LoongArch64: set LoongArch64's InstructionSet features !
+#endif // TARGET_LOONGARCH64
+
     // These calls are very important as it ensures the flags are consistent with any
     // removals specified above. This includes removing corresponding 64-bit ISAs
     // and any other implications such as SSE2 depending on SSE or AdvSimd on ArmBase
@@ -1938,6 +2018,8 @@ BOOL EEJitManager::LoadJIT()
             altJitName = MAKEDLLNAME_W(W("clrjit_unix_x86_x86"));
 #elif defined(TARGET_AMD64)
             altJitName = MAKEDLLNAME_W(W("clrjit_unix_x64_x64"));
+#elif defined(TARGET_LOONGARCH64)
+            altJitName = MAKEDLLNAME_W(W("clrjit_unix_loongarch64_loongarch64"));
 #endif
 #endif // TARGET_WINDOWS
 
@@ -2321,7 +2403,7 @@ static size_t GetDefaultReserveForJumpStubs(size_t codeHeapSize)
 {
     LIMITED_METHOD_CONTRACT;
 
-#if defined(TARGET_AMD64) || defined(TARGET_ARM64)
+#if defined(TARGET_AMD64) || defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64)
     //
     // 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.
@@ -2416,7 +2498,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)
+#if defined(TARGET_AMD64) || defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64)
     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()
@@ -2569,7 +2651,7 @@ HeapList* EEJitManager::NewCodeHeap(CodeHeapRequestInfo *pInfo, DomainCodeHeapLi
 
     size_t reserveSize = initialRequestSize;
 
-#if defined(TARGET_AMD64) || defined(TARGET_ARM64)
+#if defined(TARGET_AMD64) || defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64)
     reserveSize += JUMP_ALLOCATE_SIZE;
 #endif
 
@@ -4104,10 +4186,10 @@ 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)
-            // If we might have fragmented unwind, and we're on ARM64, make sure
-            // to returning the root record, as the trailing records don't have
-            // prolog unwind codes.
+#if defined(EXCEPTION_DATA_SUPPORTS_FUNCTION_FRAGMENTS) && (defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64))
+            // 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.
             pFunctionEntry = FindRootEntry(pFunctionEntry, baseAddress);
 #endif
 
index 6cb9edf..ca3614b 100644 (file)
@@ -478,7 +478,7 @@ 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)
+#if defined(TARGET_AMD64) || defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64)
     BYTE*               CLRPersonalityRoutine;  // jump thunk to personality routine
 #endif
 
index 0ca1032..6a65657 100644 (file)
@@ -171,16 +171,34 @@ public:
         // Shuffle float registers first
         if (m_currentFloatRegIndex < m_argLocDesc->m_cFloatReg)
         {
-            index = m_argLocDesc->m_idxFloatReg + m_currentFloatRegIndex;
-            m_currentFloatRegIndex++;
+#if defined(TARGET_LOONGARCH64)
+            if ((m_argLocDesc->m_structFields & STRUCT_FLOAT_FIELD_SECOND) && (m_currentGenRegIndex < m_argLocDesc->m_cGenReg))
+            {
+                // the first field is integer so just skip this.
+            }
+            else
+#endif
+            {
+                index = m_argLocDesc->m_idxFloatReg + m_currentFloatRegIndex;
+                m_currentFloatRegIndex++;
 
-            return index | ShuffleEntry::REGMASK | ShuffleEntry::FPREGMASK;
+                return index | ShuffleEntry::REGMASK | ShuffleEntry::FPREGMASK;
+            }
         }
 
         // Shuffle any registers first (the order matters since otherwise we could end up shuffling a stack slot
         // over a register we later need to shuffle down as well).
         if (m_currentGenRegIndex < m_argLocDesc->m_cGenReg)
         {
+#if defined(TARGET_LOONGARCH64)
+            if (7 < (m_currentGenRegIndex + m_argLocDesc->m_idxGenReg))
+            {
+                m_currentGenRegIndex++;
+                index = m_currentByteStackIndex;
+                m_currentByteStackIndex += TARGET_POINTER_SIZE;
+                return index;
+            }
+#endif
             index = m_argLocDesc->m_idxGenReg + m_currentGenRegIndex;
             m_currentGenRegIndex++;
 
index b937fd9..f6c3b5a 100644 (file)
@@ -437,7 +437,7 @@ HeapList* HostCodeHeap::InitializeHeapList(CodeHeapRequestInfo *pInfo)
 
     TrackAllocation *pTracker = NULL;
 
-#if defined(TARGET_AMD64) || defined(TARGET_ARM64)
+#if defined(TARGET_AMD64) || defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64)
 
     pTracker = AllocMemory_NoThrow(0, JUMP_ALLOCATE_SIZE, sizeof(void*), 0);
     if (pTracker == NULL)
index ef17779..03c20df 100644 (file)
@@ -1462,7 +1462,7 @@ bool EECodeManager::IsGcSafe( EECodeInfo     *pCodeInfo,
     return gcInfoDecoder.IsInterruptible();
 }
 
-#if defined(TARGET_ARM) || defined(TARGET_ARM64)
+#if defined(TARGET_ARM) || defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64)
 bool EECodeManager::HasTailCalls( EECodeInfo     *pCodeInfo)
 {
     CONTRACTL {
@@ -1480,7 +1480,7 @@ bool EECodeManager::HasTailCalls( EECodeInfo     *pCodeInfo)
 
     return gcInfoDecoder.HasTailCalls();
 }
-#endif // TARGET_ARM || TARGET_ARM64
+#endif // TARGET_ARM || TARGET_ARM64 || TARGET_LOONGARCH64
 
 #if defined(TARGET_AMD64) && defined(_DEBUG)
 
index 3f745d2..07586ac 100644 (file)
@@ -609,7 +609,7 @@ HRESULT EditAndContinueModule::ResumeInUpdatedFunction(
     SIZE_T newILOffset,
     CONTEXT *pOrigContext)
 {
-#if defined(TARGET_ARM64) || defined(TARGET_ARM)
+#if defined(TARGET_ARM64) || defined(TARGET_ARM) || defined(TARGET_LOONGARCH64)
     return E_NOTIMPL;
 #else
     LOG((LF_ENC, LL_INFO100, "EnCModule::ResumeInUpdatedFunction for %s at IL offset 0x%x, ",
index dd80bf3..04c9129 100644 (file)
@@ -6601,7 +6601,7 @@ IsDebuggerFault(EXCEPTION_RECORD *pExceptionRecord,
 
 #endif // TARGET_UNIX
 
-#ifndef TARGET_ARM64
+#if !defined(TARGET_ARM64) && !defined(TARGET_LOONGARCH64)
 EXTERN_C void JIT_StackProbe_End();
 #endif // TARGET_ARM64
 
@@ -6668,7 +6668,7 @@ bool IsIPInMarkedJitHelper(UINT_PTR uControlPc)
     CHECK_RANGE(JIT_WriteBarrier)
     CHECK_RANGE(JIT_CheckedWriteBarrier)
     CHECK_RANGE(JIT_ByRefWriteBarrier)
-#if !defined(TARGET_ARM64)
+#if !defined(TARGET_ARM64) && !defined(TARGET_LOONGARCH64)
     CHECK_RANGE(JIT_StackProbe)
 #endif // !TARGET_ARM64
 #else
@@ -6792,7 +6792,7 @@ AdjustContextForJITHelpers(
 
         Thread::VirtualUnwindToFirstManagedCallFrame(pContext);
 
-#if defined(TARGET_ARM) || defined(TARGET_ARM64)
+#if defined(TARGET_ARM) || defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64)
         // 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:
@@ -6816,7 +6816,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
+#endif // TARGET_ARM || TARGET_ARM64 || TARGET_LOONGARCH64
 
         // 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
index 90cac3a..ec40eda 100644 (file)
 #define USE_CURRENT_CONTEXT_IN_FILTER
 #endif // TARGET_X86
 
-#if defined(TARGET_ARM) || defined(TARGET_ARM64)
+#if defined(TARGET_ARM) || defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64)
 // ARM/ARM64 uses Caller-SP to locate PSPSym in the funclet frame.
 #define USE_CALLER_SP_IN_FUNCLET
-#endif // TARGET_ARM || TARGET_ARM64
+#endif // TARGET_ARM || TARGET_ARM64 || TARGET_LOONGARCH64
 
-#if defined(TARGET_ARM) || defined(TARGET_ARM64) || defined(TARGET_X86)
+#if defined(TARGET_ARM) || defined(TARGET_ARM64) || defined(TARGET_X86) || defined(TARGET_LOONGARCH64)
 #define ADJUST_PC_UNWOUND_TO_CALL
 #define STACK_RANGE_BOUNDS_ARE_CALLER_SP
 #define USE_FUNCLET_CALL_HELPER
@@ -35,7 +35,7 @@
 //
 // For x86/Linux, RtlVirtualUnwind sets EstablisherFrame as Caller-SP.
 #define ESTABLISHER_FRAME_ADDRESS_IS_CALLER_SP
-#endif // TARGET_ARM || TARGET_ARM64 || TARGET_X86
+#endif // TARGET_ARM || TARGET_ARM64 || TARGET_X86 || TARGET_LOONGARCH64
 
 #ifndef TARGET_UNIX
 void NOINLINE
@@ -528,6 +528,19 @@ void ExceptionTracker::UpdateNonvolatileRegisters(CONTEXT *pContextRecord, REGDI
     UPDATEREG(X28);
     UPDATEREG(Fp);
 
+#elif defined(TARGET_LOONGARCH64)
+
+    UPDATEREG(S0);
+    UPDATEREG(S1);
+    UPDATEREG(S2);
+    UPDATEREG(S3);
+    UPDATEREG(S4);
+    UPDATEREG(S5);
+    UPDATEREG(S6);
+    UPDATEREG(S7);
+    UPDATEREG(S8);
+    UPDATEREG(Fp);
+
 #else
     PORTABILITY_ASSERT("ExceptionTracker::UpdateNonvolatileRegisters");
 #endif
@@ -808,6 +821,8 @@ 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)
+        pContextRecord->Ra = uResumePC;
 #endif
 
         uResumePC = uAbortAddr;
@@ -1470,10 +1485,10 @@ void ExceptionTracker::InitializeCrawlFrame(CrawlFrame* pcfThisFrame, Thread* pT
     }
     else
     {
-#if defined(TARGET_ARM) || defined(TARGET_ARM64)
+#if defined(TARGET_ARM) || defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64)
         // See the comment above the call to InitRegDisplay for this assertion.
         _ASSERTE(pDispatcherContext->ControlPc == GetIP(pDispatcherContext->ContextRecord));
-#endif // TARGET_ARM || TARGET_ARM64
+#endif // TARGET_ARM || TARGET_ARM64 || TARGET_LOONGARCH64
 
 #ifdef ESTABLISHER_FRAME_ADDRESS_IS_CALLER_SP
         // Simply setup the callerSP during the second pass in the caller context.
@@ -3249,6 +3264,8 @@ static inline UINT_PTR *GetFirstNonVolatileRegisterAddress(PCONTEXT pContextReco
     return (UINT_PTR*)&(pContextRecord->R4);
 #elif defined(TARGET_ARM64)
     return (UINT_PTR*)&(pContextRecord->X19);
+#elif defined(TARGET_LOONGARCH64)
+    return (UINT_PTR*)&(pContextRecord->S0);
 #elif defined(TARGET_X86)
     return (UINT_PTR*)&(pContextRecord->Edi);
 #else
@@ -3259,7 +3276,7 @@ static inline UINT_PTR *GetFirstNonVolatileRegisterAddress(PCONTEXT pContextReco
 
 static inline TADDR GetFrameRestoreBase(PCONTEXT pContextRecord)
 {
-#if defined(TARGET_ARM) || defined(TARGET_ARM64)
+#if defined(TARGET_ARM) || defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64)
     return GetSP(pContextRecord);
 #elif defined(TARGET_X86)
     return pContextRecord->Ebp;
@@ -5284,8 +5301,8 @@ BOOL HandleHardwareException(PAL_SEHException* ex)
         Thread *pThread = GetThreadNULLOk();
         if (pThread != NULL && g_pDebugInterface != NULL)
         {
-#if (defined(TARGET_ARM) || defined(TARGET_ARM64))
-            // On ARM and ARM64 exception point to the break instruction.
+#if (defined(TARGET_ARM) || defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64))
+            // 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
             // However, the rest of the code expects that it points to an instruction after the break.
@@ -5500,7 +5517,7 @@ void FixupDispatcherContext(DISPATCHER_CONTEXT* pDispatcherContext, CONTEXT* pCo
 
     pDispatcherContext->ControlPc             = (UINT_PTR) GetIP(pDispatcherContext->ContextRecord);
 
-#if defined(TARGET_ARM) || defined(TARGET_ARM64)
+#if defined(TARGET_ARM) || defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64)
     // 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
@@ -5523,12 +5540,15 @@ void FixupDispatcherContext(DISPATCHER_CONTEXT* pDispatcherContext, CONTEXT* pCo
 #ifdef TARGET_ARM
     // But keep the architecture flag set (its part of CONTEXT_DEBUG_REGISTERS)
     pDispatcherContext->ContextRecord->ContextFlags |= CONTEXT_ARM;
+#elif defined(TARGET_LOONGARCH64)
+    // But keep the architecture flag set (its part of CONTEXT_DEBUG_REGISTERS)
+    pDispatcherContext->ContextRecord->ContextFlags |= CONTEXT_LOONGARCH64;
 #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
+#endif // TARGET_ARM || TARGET_ARM64 || TARGET_LOONGARCH64
 
     INDEBUG(pDispatcherContext->FunctionEntry = (PT_RUNTIME_FUNCTION)INVALID_POINTER_CD);
     INDEBUG(pDispatcherContext->ImageBase     = INVALID_POINTER_CD);
index 38bbc80..1dfa92b 100644 (file)
@@ -860,6 +860,9 @@ public:
 #elif defined(TARGET_ARM64)
             Object** firstIntReg = (Object**)&this->GetContext()->X0;
             Object** lastIntReg  = (Object**)&this->GetContext()->X28;
+#elif defined(TARGET_LOONGARCH64)
+            Object** firstIntReg = (Object**)&this->GetContext()->Tp;
+            Object** lastIntReg  = (Object**)&this->GetContext()->S8;
 #else
             _ASSERTE(!"nyi for platform");
 #endif
@@ -1901,6 +1904,10 @@ protected:
     TADDR           m_ReturnAddress;
     TADDR           m_x8; // ret buff arg
     ArgumentRegisters m_argumentRegisters;
+#elif defined (TARGET_LOONGARCH64)
+    TADDR           m_fp;
+    TADDR           m_ReturnAddress;
+    ArgumentRegisters m_argumentRegisters;
 #else
     TADDR           m_ReturnAddress;  // return address into unmanaged code
 #endif
index b0e6aa9..591c611 100644 (file)
@@ -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)
+#if defined(TARGET_ARM) || defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64)
 static void replaceSafePointInstructionWithGcStressInstr(UINT32 safePointOffset, LPVOID codeStart);
 static bool replaceInterruptibleRangesWithGcStressInstr (UINT32 startOffset, UINT32 stopOffset, LPVOID codeStart);
 #endif
@@ -97,7 +97,7 @@ bool IsGcCoverageInterruptInstruction(PBYTE instrPtr)
 {
     UINT32 instrVal;
 
-#if defined(TARGET_ARM64)
+#if defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64)
     instrVal = *reinterpret_cast<UINT32*>(instrPtr);
 #elif defined(TARGET_ARM)
     size_t instrLen = GetARMInstructionLength(instrPtr);
@@ -118,7 +118,7 @@ bool IsGcCoverageInterruptInstruction(PBYTE instrPtr)
 
 bool IsOriginalInstruction(PBYTE instrPtr, GCCoverageInfo* gcCover, DWORD offset)
 {
-#if defined(TARGET_ARM64)
+#if defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64)
     UINT32 instrVal = *reinterpret_cast<UINT32*>(instrPtr);
     UINT32 origInstrVal = *reinterpret_cast<UINT32*>(gcCover->savedCode + offset);
     return (instrVal == origInstrVal);
@@ -174,7 +174,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)
+#if !defined(TARGET_ARM) && !defined(TARGET_ARM64)  && !defined(TARGET_LOONGARCH64)
     if (gcCover->methodRegion.coldSize != 0)
     {
         gcCover->SprinkleBreakpoints(gcCover->savedCode + gcCover->methodRegion.hotSize,
@@ -335,6 +335,12 @@ void ReplaceInstrAfterCall(PBYTE instrToReplace, MethodDesc* callMD)
     {
         *instrToReplace = INTERRUPT_INSTR;
     }
+#elif defined(TARGET_LOONGARCH64)
+    bool protectReturn = ispointerKind;
+    if (protectReturn)
+        *(DWORD*)instrToReplace = INTERRUPT_INSTR_PROTECT_RET;
+    else
+        *(DWORD*)instrToReplace = INTERRUPT_INSTR;
 #else
     _ASSERTE(!"not implemented for platform");
 #endif
@@ -618,7 +624,7 @@ void GCCoverageInfo::SprinkleBreakpoints(
     if ((regionOffsetAdj==0) && (*codeStart != INTERRUPT_INSTR))
         doingEpilogChecks = false;
 
-#elif defined(TARGET_ARM) || defined(TARGET_ARM64)
+#elif defined(TARGET_ARM) || defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64)
     //Save the method code from hotRegion
     memcpy(saveAddr, (BYTE*)methodRegion.hotStartAddress, methodRegion.hotSize);
 
@@ -662,7 +668,7 @@ void GCCoverageInfo::SprinkleBreakpoints(
 #endif // TARGET_X86
 }
 
-#if defined(TARGET_ARM) || defined(TARGET_ARM64)
+#if defined(TARGET_ARM) || defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64)
 
 #ifdef PARTIALLY_INTERRUPTIBLE_GC_SUPPORTED
 
@@ -749,6 +755,20 @@ void replaceSafePointInstructionWithGcStressInstr(UINT32 safePointOffset, LPVOID
     {
         instructionIsACallThroughRegister = TRUE;
     }
+#elif defined(TARGET_LOONGARCH64)
+    DWORD instr = *((DWORD*)savedInstrPtr - 1);
+
+    // Is the call through a register or an immediate offset
+    // bl
+    if (((instr >> 26) & 0x3F) == 0x15)
+    {
+        instructionIsACallThroughImmediate = TRUE;
+    }
+    // jirl
+    else if (((instr >> 26) & 0x3F) == 0x13)
+    {
+        instructionIsACallThroughRegister = TRUE;
+    }
 #endif  // _TARGET_XXXX_
 
     // safe point must always be after a call instruction
@@ -772,7 +792,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)
+#elif defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64)
         *((DWORD*)instrPtrWriterHolder.GetRW()) = INTERRUPT_INSTR_CALL;
 #endif // _TARGET_XXXX_
     }
@@ -893,7 +913,7 @@ bool replaceInterruptibleRangesWithGcStressInstr (UINT32 startOffset, UINT32 sto
             }
 
             instrPtrRW += instrLen;
-#elif defined(TARGET_ARM64)
+#elif defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64)
             *((DWORD*)instrPtrRW) = INTERRUPT_INSTR;
             instrPtrRW += 4;
 #endif // TARGET_XXXX_
@@ -975,6 +995,26 @@ static PBYTE getTargetOfCall(PBYTE instrPtr, PCONTEXT regs, PBYTE* nextInstr) {
    {
        return 0; // Fail
    }
+#elif defined(TARGET_LOONGARCH64)
+    if ((((*reinterpret_cast<DWORD*>(instrPtr)) >> 26) & 0x3F) == 0x15)
+    {
+        int imm26 = (((*reinterpret_cast<DWORD*>(instrPtr)) & 0x3ff) << 16) | (((*reinterpret_cast<DWORD*>(instrPtr)) >> 10) & 0xffff);
+        *nextInstr = instrPtr + 4;
+        return PC + imm26;
+    }
+    else if ((((*reinterpret_cast<DWORD*>(instrPtr)) >> 26) & 0x3F) == 0x13)
+    {
+        // call through register
+        *nextInstr = instrPtr + 4;
+
+        assert((((*reinterpret_cast<DWORD*>(instrPtr)) >> 10) & 0xffff) == 0);
+        unsigned int regnum = ((*reinterpret_cast<DWORD*>(instrPtr)) >> 5) & 0x1F;
+        return (BYTE *)getRegVal(regnum, regs);
+    }
+    else
+    {
+        return 0; // Fail
+    }
 #endif
 
 #ifdef TARGET_AMD64
@@ -1214,6 +1254,8 @@ void RemoveGcCoverageInterrupt(TADDR instrPtr, BYTE * savedInstrPtr, GCCoverageI
         *(DWORD *)instrPtrWriterHolder.GetRW() = *(DWORD *)savedInstrPtr;
 #elif defined(TARGET_ARM64)
     *(DWORD *)instrPtrWriterHolder.GetRW() = *(DWORD *)savedInstrPtr;
+#elif defined(TARGET_LOONGARCH64)
+    *(DWORD *)instrPtrWriterHolder.GetRW() = *(DWORD *)savedInstrPtr;
 #else
     *(BYTE *)instrPtrWriterHolder.GetRW() = *savedInstrPtr;
 #endif
@@ -1428,6 +1470,12 @@ void DoGcStress (PCONTEXT regs, NativeCodeVersion nativeCodeVersion)
     atCall = (instrVal == INTERRUPT_INSTR_CALL);
     afterCallProtect[0] = (instrVal == INTERRUPT_INSTR_PROTECT_RET);
 
+#elif defined(TARGET_LOONGARCH64)
+    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_*
 
     if (!IsGcCoverageInterruptInstruction(instrPtr))
@@ -1546,7 +1594,7 @@ void DoGcStress (PCONTEXT regs, NativeCodeVersion nativeCodeVersion)
     }
 #endif // TARGET_X86
 
-#if defined(TARGET_X86) || defined(TARGET_AMD64) || defined(TARGET_ARM) || defined(TARGET_ARM64)
+#if defined(TARGET_X86) || defined(TARGET_AMD64) || defined(TARGET_ARM) || defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64)
 
     /* 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
@@ -1600,6 +1648,8 @@ void DoGcStress (PCONTEXT regs, NativeCodeVersion nativeCodeVersion)
                     *(DWORD*)nextInstrWriterHolder.GetRW() = INTERRUPT_INSTR_32;
 #elif defined(TARGET_ARM64)
                 *(DWORD*)nextInstrWriterHolder.GetRW() = INTERRUPT_INSTR;
+#elif defined(TARGET_LOONGARCH64)
+                *(DWORD*)nextInstrWriterHolder.GetRW() = INTERRUPT_INSTR;
 #else
                 *nextInstrWriterHolder.GetRW() = INTERRUPT_INSTR;
 #endif
@@ -1681,6 +1731,8 @@ void DoGcStress (PCONTEXT regs, NativeCodeVersion nativeCodeVersion)
         retValRegs[numberOfRegs++] = regs->R0;
 #elif defined(TARGET_ARM64)
         retValRegs[numberOfRegs++] = regs->X0;
+#elif defined(TARGET_LOONGARCH64)
+        retValRegs[numberOfRegs++] = regs->A0;
 #endif // TARGET_ARM64
     }
 
@@ -1732,6 +1784,8 @@ void DoGcStress (PCONTEXT regs, NativeCodeVersion nativeCodeVersion)
             regs->R0 = retValRegs[0];
 #elif defined(TARGET_ARM64)
             regs->X[0] = retValRegs[0];
+#elif defined(TARGET_LOONGARCH64)
+            regs->A0 = retValRegs[0];
 #else
             PORTABILITY_ASSERT("DoGCStress - return register");
 #endif
index abd07de..3520737 100644 (file)
@@ -105,6 +105,13 @@ typedef DPTR(GCCoverageInfo) PTR_GCCoverageInfo; // see code:GCCoverageInfo::sav
 #define INTERRUPT_INSTR_CALL            0xBADC0DE1
 #define INTERRUPT_INSTR_PROTECT_RET     0xBADC0DE2
 
+#elif defined(TARGET_LOONGARCH64)
+
+// The following encodings are undefined.
+#define INTERRUPT_INSTR                 0xffffff0f
+#define INTERRUPT_INSTR_CALL            0xffffff0e
+#define INTERRUPT_INSTR_PROTECT_RET     0xffffff0d
+
 #endif // _TARGET_*
 
 // The body of this method is in this header file to allow
@@ -124,6 +131,18 @@ inline bool IsGcCoverageInterruptInstructionVal(UINT32 instrVal)
         return false;
     }
 
+#elif defined(TARGET_LOONGARCH64)
+
+    switch (instrVal)
+    {
+    case INTERRUPT_INSTR:
+    case INTERRUPT_INSTR_CALL:
+    case INTERRUPT_INSTR_PROTECT_RET:
+        return true;
+    default:
+        return false;
+    }
+
 #elif defined(TARGET_ARM)
 
     UINT16 instrVal16 = static_cast<UINT16>(instrVal);
index 2085ff2..1e20632 100644 (file)
@@ -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)
+#if defined(HOST_ARM64) || defined(HOST_ARM) || defined(HOST_LOONGARCH64)
         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)
+#if defined(HOST_ARM64) || defined(HOST_ARM) || defined(HOST_LOONGARCH64)
         // 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)
+#if defined(HOST_ARM64) || defined(HOST_ARM) || defined(HOST_LOONGARCH64)
         is_runtime_suspended = (stompWBCompleteActions & SWB_EE_RESTART) || is_runtime_suspended;
         if (!is_runtime_suspended)
         {
index 7914aac..9018c66 100644 (file)
@@ -132,7 +132,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)
+#elif defined(TARGET_ARM) || defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64)
     m_HasTailCalls             = ((headerFlags & GC_INFO_HAS_TAILCALLS) != 0);
 #endif // TARGET_AMD64
     int hasSizeOfEditAndContinuePreservedArea = headerFlags & GC_INFO_HAS_EDIT_AND_CONTINUE_PRESERVED_SLOTS;
@@ -363,7 +363,7 @@ bool GcInfoDecoder::IsSafePoint(UINT32 codeOffset)
     if(m_NumSafePoints == 0)
         return false;
 
-#if defined(TARGET_AMD64) || defined(TARGET_ARM) || defined(TARGET_ARM64)
+#if defined(TARGET_AMD64) || defined(TARGET_ARM) || defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64)
     // Safepoints are encoded with a -1 adjustment
     codeOffset--;
 #endif
@@ -383,7 +383,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)
+#if defined(TARGET_ARM) || defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64)
     // 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
@@ -430,7 +430,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)
+#if defined(TARGET_AMD64) || defined(TARGET_ARM) || defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64)
         // Safepoints are encoded with a -1 adjustment
         offset--;
 #endif
@@ -522,13 +522,13 @@ bool GcInfoDecoder::GetIsVarArg()
     return m_IsVarArg;
 }
 
-#if defined(TARGET_ARM) || defined(TARGET_ARM64)
+#if defined(TARGET_ARM) || defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64)
 bool GcInfoDecoder::HasTailCalls()
 {
     _ASSERTE( m_Flags & DECODE_HAS_TAILCALLS );
     return m_HasTailCalls;
 }
-#endif // TARGET_ARM || TARGET_ARM64
+#endif // TARGET_ARM || TARGET_ARM64 || TARGET_LOONGARCH64
 
 bool GcInfoDecoder::WantsReportOnlyLeaf()
 {
@@ -1744,6 +1744,133 @@ OBJECTREF* GcInfoDecoder::GetCapturedRegister(
 }
 #endif // TARGET_UNIX && !FEATURE_REDHAWK
 
+#elif defined(TARGET_LOONGARCH64)
+
+#if defined(TARGET_UNIX) && !defined(FEATURE_REDHAWK)
+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_REDHAWK
+
+OBJECTREF* GcInfoDecoder::GetRegisterSlot(
+                        int             regNum,
+                        PREGDISPLAY     pRD
+                        )
+{
+    _ASSERTE((regNum == 1) || (regNum >= 4 && regNum <= 31));
+
+#ifdef FEATURE_REDHAWK
+    PTR_UIntNative* ppReg = &pRD->pR0;
+
+    return (OBJECTREF*)*(ppReg + regNum);
+#else
+    if(regNum == 1)
+    {
+        return (OBJECTREF*) pRD->pCurrentContextPointers->Ra;
+    }
+    else if(regNum == 22)
+    {
+        return (OBJECTREF*) pRD->pCurrentContextPointers->Fp;
+    }
+    else if (regNum < 22)
+    {
+        return (OBJECTREF*)*(DWORD64**)(&pRD->volatileCurrContextPointers.A0 + (regNum - 4));//A0=4.
+    }
+
+    return (OBJECTREF*)*(DWORD64**)(&pRD->pCurrentContextPointers->S0 + (regNum-23));
+#endif
+}
+
+bool GcInfoDecoder::IsScratchRegister(int regNum,  PREGDISPLAY pRD)
+{
+    _ASSERTE(regNum >= 0 && regNum <= 31);
+
+    return (regNum <= 21 && regNum >= 4);
+}
+
+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_REDHAWK) && !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(
@@ -1829,6 +1956,8 @@ int GcInfoDecoder::GetStackReg(int spBase)
     int esp = 13;
 #elif defined(TARGET_ARM64)
     int esp = 31;
+#elif defined(TARGET_LOONGARCH64)
+    int esp = 3;
 #endif
 
     if( GC_SP_REL == spBase )
index 3f20e5c..d193927 100644 (file)
@@ -3672,6 +3672,8 @@ Elf64_Ehdr::Elf64_Ehdr()
     e_machine = EM_X86_64;
 #elif defined(TARGET_ARM64)
     e_machine = EM_AARCH64;
+#elif defined(TARGET_LOONGARCH64)
+    e_machine = EM_LOONGARCH;
 #endif
     e_flags = 0;
     e_version = 1;
index 82f6fad..0200c71 100644 (file)
@@ -91,7 +91,7 @@ InterpreterMethodInfo::InterpreterMethodInfo(CEEInfo* comp, CORINFO_METHOD_INFO*
     }
 #endif
 
-#if defined(UNIX_AMD64_ABI)
+#if defined(UNIX_AMD64_ABI) || defined(HOST_LOONGARCH64)
     // ...or it fits into two registers.
     if (hasRetBuff && getClassSize(methInfo->args.retTypeClass) <= 2 * sizeof(void*))
     {
@@ -534,6 +534,9 @@ void Interpreter::ArgState::AddArg(unsigned canonIndex, short numSlots, bool noR
         // On ARM, args are pushed in *reverse* order.  So we will create an offset relative to the address
         // of the first stack arg; later, we will add the size of the non-stack arguments.
         ClrSafeInt<short> offset(callerArgStackSlots);
+#elif defined(HOST_LOONGARCH64)
+        callerArgStackSlots += numSlots;
+        ClrSafeInt<short> offset(-callerArgStackSlots);
 #endif
         offset *= static_cast<short>(sizeof(void*));
         _ASSERTE(!offset.IsOverflow());
@@ -685,6 +688,19 @@ void Interpreter::ArgState::AddFPArg(unsigned canonIndex, unsigned short numSlot
     }
     numFPRegArgSlots += numSlots;
 
+#elif defined(HOST_LOONGARCH64)
+
+    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
@@ -882,7 +898,6 @@ CorJitResult Interpreter::GenerateInterpreterStub(CEEInfo* comp,
         // x8 through x15 are scratch registers on ARM64.
         IntReg x8 = IntReg(8);
         IntReg x9 = IntReg(9);
-#else
 #error unsupported platform
 #endif
     }
@@ -1110,7 +1125,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)
+#elif defined(HOST_AMD64) || defined(HOST_ARM64) || defined(HOST_LOONGARCH64)
                     argState.AddArg(k);
 #else
 #error unknown platform
@@ -1123,7 +1138,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)
+#elif defined(HOST_AMD64) || defined(HOST_ARM64) || defined(HOST_LOONGARCH64)
                     argState.AddFPArg(k, 1, false);
 #else
 #error unknown platform
@@ -1136,7 +1151,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)
+#elif defined(HOST_AMD64) || defined(HOST_ARM64) || defined(HOST_LOONGARCH64)
                     argState.AddFPArg(k, 1, false);
 #else
 #error unknown platform
@@ -1177,6 +1192,8 @@ CorJitResult Interpreter::GenerateInterpreterStub(CEEInfo* comp,
 #endif
                                     );
                         }
+#elif defined(HOST_LOONGARCH64)
+                        argState.AddArg(k, static_cast<short>(szSlots));
 #else
 #error unknown platform
 #endif
@@ -1229,6 +1246,10 @@ CorJitResult Interpreter::GenerateInterpreterStub(CEEInfo* comp,
             unsigned short stackArgBaseOffset = (2 + argState.numRegArgs + argState.numFPRegArgSlots) * sizeof(void*);
 #elif defined(HOST_AMD64)
             unsigned short stackArgBaseOffset = (argState.numRegArgs) * sizeof(void*);
+#elif defined(HOST_LOONGARCH64)
+            // 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*));
 #else
 #error unsupported platform
 #endif
@@ -1277,6 +1298,8 @@ CorJitResult Interpreter::GenerateInterpreterStub(CEEInfo* comp,
                     X86Reg argRegs[] = { kECX, kEDX, kR8, kR9 };
                     if (!jmpCall) { sl.X86EmitIndexRegStoreRSP(regArgsFound * sizeof(void*), argRegs[regArgsFound - 1]); }
                     argState.argOffsets[k] = (regArgsFound - 1) * sizeof(void*);
+#elif defined(HOST_LOONGARCH64)
+                    argState.argOffsets[k] += intRegArgBaseOffset;
 #else
 #error unsupported platform
 #endif
@@ -1589,6 +1612,8 @@ CorJitResult Interpreter::GenerateInterpreterStub(CEEInfo* comp,
 
         sl.EmitEpilog();
 
+#elif defined(HOST_LOONGARCH64)
+        assert(!"unimplemented on LOONGARCH yet");
 
 #else
 #error unsupported platform
@@ -6280,6 +6305,9 @@ void Interpreter::MkRefany()
 #elif defined(HOST_ARM64)
     tbr = NULL;
     NYI_INTERP("Unimplemented code: MkRefAny");
+#elif defined(HOST_LOONGARCH64)
+    tbr = NULL;
+    NYI_INTERP("Unimplemented code: MkRefAny on LOONGARCH");
 #else
 #error "unsupported platform"
 #endif
@@ -9424,6 +9452,8 @@ void Interpreter::DoCallWork(bool virtualCall, void* thisArg, CORINFO_RESOLVED_T
     unsigned totalArgSlots = nSlots + HFAReturnArgSlots;
 #elif defined(HOST_AMD64)
     unsigned totalArgSlots = nSlots;
+#elif defined(HOST_LOONGARCH64)
+    unsigned totalArgSlots = nSlots;
 #else
 #error "unsupported platform"
 #endif
index 0414848..7856f97 100644 (file)
@@ -1002,6 +1002,8 @@ private:
 #else
         static const int MaxNumFPRegArgSlots = 4;
 #endif
+#elif defined(HOST_LOONGARCH64)
+        static const int MaxNumFPRegArgSlots = 8;
 #endif
 
         ~ArgState()
@@ -2062,6 +2064,8 @@ unsigned short Interpreter::NumberOfIntegerRegArgs()
 unsigned short Interpreter::NumberOfIntegerRegArgs() { return 4; }
 #elif defined(HOST_ARM64)
 unsigned short Interpreter::NumberOfIntegerRegArgs() { return 8; }
+#elif defined(HOST_LOONGARCH64)
+unsigned short Interpreter::NumberOfIntegerRegArgs() { return 8; }
 #else
 #error Unsupported architecture.
 #endif
index 75f3a38..24aea8f 100644 (file)
@@ -9503,434 +9503,11 @@ uint32_t CEEInfo::getLoongArch64PassStructInRegisterFlags(CORINFO_CLASS_HANDLE c
 
     JIT_TO_EE_TRANSITION_LEAF();
 
-    TypeHandle th(cls);
-
-    bool useNativeLayout           = false;
     uint32_t 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) || (fieldType == ELEMENT_TYPE_R8))
-                        size = STRUCT_FLOAT_FIELD_ONLY_ONE;
-                }
-                else if (fieldType == ELEMENT_TYPE_VALUETYPE)
-                {
-                    pMethodTable  = pFieldStart->GetFieldTypeHandleThrowing().GetMethodTable();
-                    if (pMethodTable->GetNumIntroducedInstanceFields() == 1)
-                    {
-                        size = getLoongArch64PassStructInRegisterFlags((CORINFO_CLASS_HANDLE)pMethodTable);
-                    }
-                    else if (pMethodTable->GetNumIntroducedInstanceFields() == 2)
-                    {
-                        size = getLoongArch64PassStructInRegisterFlags((CORINFO_CLASS_HANDLE)pMethodTable);
-                    }
-                }
-            }
-            else if (numIntroducedFields == 2)
-            {
-                FieldDesc *pFieldStart = pMethodTable->GetApproxFieldDescListRaw();
-
-                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;
-
-                }
-                else if (fieldType == ELEMENT_TYPE_VALUETYPE)
-                {
-                    pMethodTable  = pFieldStart->GetFieldTypeHandleThrowing().GetMethodTable();
-                    if (pMethodTable->GetNumIntroducedInstanceFields() == 1)
-                    {
-                        size = getLoongArch64PassStructInRegisterFlags((CORINFO_CLASS_HANDLE)pMethodTable);
-                        if (size == STRUCT_FLOAT_FIELD_ONLY_ONE)
-                        {
-                            size = pFieldStart[0].GetSize() == 8 ? STRUCT_FIRST_FIELD_DOUBLE : STRUCT_FLOAT_FIELD_FIRST;
-                        }
-                        else if (size == STRUCT_NO_FLOAT_FIELD)
-                        {
-                            size = pFieldStart[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 (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 (pFieldStart[1].GetSize() == 8)
-                        size |= STRUCT_SECOND_FIELD_SIZE_IS8;
-                }
-                else if (fieldType == ELEMENT_TYPE_VALUETYPE)
-                {
-                    pMethodTable  = pFieldStart[1].GetFieldTypeHandleThrowing().GetMethodTable();
-                    if (pMethodTable->GetNumIntroducedInstanceFields() == 1)
-                    {
-                        DWORD size2 = getLoongArch64PassStructInRegisterFlags((CORINFO_CLASS_HANDLE)pMethodTable);
-                        if (size2 == STRUCT_FLOAT_FIELD_ONLY_ONE)
-                        {
-                            if (pFieldStart[1].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 (size2 == 0)
-                        {
-                            size |= pFieldStart[1].GetSize() == 8 ? STRUCT_SECOND_FIELD_SIZE_IS8 : 0;
-                        }
-                        else
-                        {
-                            size = STRUCT_NO_FLOAT_FIELD;
-                            goto _End_arg;
-                        }
-                    }
-                    else
-                    {
-                        size = STRUCT_NO_FLOAT_FIELD;
-                        goto _End_arg;
-                    }
-                }
-                else if (pFieldStart[1].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->GetNumIntroducedInstanceFields();
-            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;
-                        else if (numIntroducedFields == 2)
-                            size = STRUCT_FIELD_TWO_DOUBLES;
-                        goto _End_arg;
-                    }
-                }
-
-                if (CorTypeInfo::IsPrimitiveType_NoThrow(fieldType))
-                {
-                    if ((fieldType == ELEMENT_TYPE_R4) || (fieldType == ELEMENT_TYPE_R8))
-                        size = STRUCT_FLOAT_FIELD_ONLY_ONE;
-                }
-                else if (fieldType == ELEMENT_TYPE_VALUETYPE)
-                {
-                    const NativeFieldDescriptor *pNativeFieldDescs = pMethodTable->GetNativeLayoutInfo()->GetNativeFieldDescriptors();
-                    if (pNativeFieldDescs->GetCategory() == NativeFieldCategory::NESTED)
-                    {
-                        size = getLoongArch64PassStructInRegisterFlags((CORINFO_CLASS_HANDLE)pNativeFieldDescs->GetNestedNativeMethodTable());
-                        return size;
-                    }
-                    else if (pNativeFieldDescs->GetCategory() == NativeFieldCategory::FLOAT)
-                    {
-                        if (pNativeFieldDescs->NativeSize() == 4)
-                        {
-                            size = STRUCT_FLOAT_FIELD_ONLY_ONE;
-                        }
-                        else if (pNativeFieldDescs->NativeSize() == 8)
-                        {
-                            size = STRUCT_FLOAT_FIELD_ONLY_ONE;
-                        }
-                        else
-                        {
-                            UNREACHABLE_MSG("Invalid NativeFieldCategory.----LoongArch64----");
-                        }
-                    }
-                    else
-                    {
-                        pMethodTable = pNativeFieldDescs->GetNestedNativeMethodTable();
-                        if (pNativeFieldDescs->GetNumElements() == 1)
-                        {
-                            size = getLoongArch64PassStructInRegisterFlags((CORINFO_CLASS_HANDLE)pMethodTable);
-                        }
-                        else if (pNativeFieldDescs->GetNumElements() == 2)
-                        {
-                            size = getLoongArch64PassStructInRegisterFlags((CORINFO_CLASS_HANDLE)pMethodTable);
-                        }
-                    }
-                }
-            }
-            else if (numIntroducedFields == 2)
-            {
-                pFieldStart = pMethodTable->GetApproxFieldDescListRaw();
-
-                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 (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();
-
-                    if (pNativeFieldDescs->GetCategory() == NativeFieldCategory::NESTED)
-                    {
-                        MethodTable* pMethodTable2 = pNativeFieldDescs->GetNestedNativeMethodTable();
-
-                        if ((pMethodTable2->GetNumInstanceFieldBytes() > 8) || (pMethodTable2->GetNumIntroducedInstanceFields() > 1))
-                            goto _End_arg;
-                        size = getLoongArch64PassStructInRegisterFlags((CORINFO_CLASS_HANDLE)pMethodTable2);
-                        if (size == STRUCT_FLOAT_FIELD_ONLY_ONE)
-                        {
-                            if (pFieldStart[0].GetSize() == 8)
-                                size = STRUCT_FIRST_FIELD_DOUBLE;
-                            else
-                                size = STRUCT_FLOAT_FIELD_FIRST;
-                        }
-                        else if (pFieldStart[0].GetSize() == 8)
-                        {
-                            size = STRUCT_FIRST_FIELD_SIZE_IS8;
-                        }
-                        else
-                            size = STRUCT_NO_FLOAT_FIELD;
-                    }
-                    else if (pNativeFieldDescs->GetCategory() == NativeFieldCategory::FLOAT)
-                    {
-                        if (pNativeFieldDescs->NativeSize() == 4)
-                        {
-                            size = STRUCT_FLOAT_FIELD_FIRST;
-                        }
-                        else if (pNativeFieldDescs->NativeSize() == 8)
-                        {
-                            size = STRUCT_FIRST_FIELD_DOUBLE;
-                        }
-                        else
-                        {
-                            UNREACHABLE_MSG("Invalid NativeFieldCategory.----LoongArch64----2");
-                        }
-                    }
-                    else
-                    {
-                        MethodTable* pMethodTable2 = pFieldStart[0].GetFieldTypeHandleThrowing().AsMethodTable();
-                        if (pMethodTable2->GetNumIntroducedInstanceFields() == 1)
-                        {
-                            size = getLoongArch64PassStructInRegisterFlags((CORINFO_CLASS_HANDLE)pMethodTable2);
-                            if (size == STRUCT_FLOAT_FIELD_ONLY_ONE)
-                            {
-                                if (pFieldStart[0].GetSize() == 8)
-                                    size = STRUCT_FIRST_FIELD_DOUBLE;
-                                else
-                                    size = STRUCT_FLOAT_FIELD_FIRST;
-                            }
-                            else if (pFieldStart[0].GetSize() == 8)
-                            {
-                                size = STRUCT_FIRST_FIELD_SIZE_IS8;
-                            }
-                            else
-                                size = STRUCT_NO_FLOAT_FIELD;
-                        }
-                        else
-                            goto _End_arg;
-                    }
-                }
-                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 (pFieldStart[1].GetSize() == 8)
-                        size |= STRUCT_SECOND_FIELD_SIZE_IS8;
-                }
-                else if (fieldType == ELEMENT_TYPE_VALUETYPE)
-                {
-                    MethodTable* pMethodTable2 = pFieldStart[1].GetFieldTypeHandleThrowing().AsMethodTable();
-                    if ((pMethodTable2->GetNumInstanceFieldBytes() > 8) || (pMethodTable2->GetNumIntroducedInstanceFields() > 1))
-                    {
-                        size = STRUCT_NO_FLOAT_FIELD;
-                        goto _End_arg;
-                    }
-                    if (pMethodTable2->HasLayout())
-                    {
-                        const NativeFieldDescriptor *pNativeFieldDescs = pMethodTable2->GetNativeLayoutInfo()->GetNativeFieldDescriptors();
-
-                        if (pNativeFieldDescs->NativeSize() > 8)
-                        {
-                            size = STRUCT_NO_FLOAT_FIELD;
-                            goto _End_arg;
-                        }
-
-                        if (pNativeFieldDescs->GetCategory() == NativeFieldCategory::NESTED)
-                        {
-                            pMethodTable = pNativeFieldDescs->GetNestedNativeMethodTable();
-
-                            if (pMethodTable->GetNumIntroducedInstanceFields() > 1)
-                            {
-                                size = STRUCT_NO_FLOAT_FIELD;
-                                goto _End_arg;
-                            }
-
-                            if (getLoongArch64PassStructInRegisterFlags((CORINFO_CLASS_HANDLE)pMethodTable) == STRUCT_FLOAT_FIELD_ONLY_ONE)
-                            {
-                                if (pMethodTable->GetNumInstanceFieldBytes() == 4)
-                                    size = size & STRUCT_FLOAT_FIELD_FIRST ? (size ^ STRUCT_MERGE_FIRST_SECOND) : (size | STRUCT_FLOAT_FIELD_SECOND);
-                                else if (pMethodTable->GetNumInstanceFieldBytes() == 8)
-                                    size = size & STRUCT_FLOAT_FIELD_FIRST ? (size ^ STRUCT_MERGE_FIRST_SECOND_8) : (size | STRUCT_SECOND_FIELD_DOUBLE);
-                            }
-                            else if (pMethodTable->GetNumInstanceFieldBytes() == 8)
-                                size |= STRUCT_SECOND_FIELD_SIZE_IS8;
-                            else
-                            {
-                                size = STRUCT_NO_FLOAT_FIELD;
-                            }
-                        }
-                        else if (pNativeFieldDescs->GetCategory() == NativeFieldCategory::FLOAT)
-                        {
-                            if (pNativeFieldDescs->NativeSize() == 4)
-                            {
-                                size = size & STRUCT_FLOAT_FIELD_FIRST ? (size ^ STRUCT_MERGE_FIRST_SECOND) : (size | STRUCT_FLOAT_FIELD_SECOND);
-                            }
-                            else if (pNativeFieldDescs->NativeSize() == 8)
-                            {
-                                size = size & STRUCT_FLOAT_FIELD_FIRST ? (size ^ STRUCT_MERGE_FIRST_SECOND_8) : (size | STRUCT_SECOND_FIELD_DOUBLE);
-                            }
-                            else
-                            {
-                                UNREACHABLE_MSG("Invalid NativeFieldCategory.----LoongArch64----3");
-                            }
-                        }
-                        else
-                        {
-                            if (pNativeFieldDescs->GetNumElements() == 1)
-                            {
-                                fieldType = pNativeFieldDescs->GetFieldDesc()[0].GetFieldType();
-                                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;
-                                    goto _End_arg;
-                                }
-                                else if (pNativeFieldDescs->NativeSize() == 8)
-                                    size |= STRUCT_SECOND_FIELD_SIZE_IS8;
-                            }
-                            else
-                            {
-                                size = STRUCT_NO_FLOAT_FIELD;
-                            }
-                        }
-                    }
-                    else
-                    {
-                        if (getLoongArch64PassStructInRegisterFlags((CORINFO_CLASS_HANDLE)pMethodTable2) == 1)
-                        {
-                            if (pMethodTable2->GetNumInstanceFieldBytes() == 4)
-                                size = size & STRUCT_FLOAT_FIELD_FIRST ? (size ^ STRUCT_MERGE_FIRST_SECOND) : (size | STRUCT_FLOAT_FIELD_SECOND);
-                            else if (pMethodTable2->GetNumInstanceFieldBytes() == 8)
-                                size = size & STRUCT_FLOAT_FIELD_FIRST ? (size ^ STRUCT_MERGE_FIRST_SECOND_8) : (size | STRUCT_SECOND_FIELD_DOUBLE);
-                        }
-                        else if (pMethodTable2->GetNumInstanceFieldBytes() == 8)
-                            size |= STRUCT_SECOND_FIELD_SIZE_IS8;
-                    }
-                }
-                else if (pFieldStart[1].GetSize() == 8)
-                    size |= STRUCT_SECOND_FIELD_SIZE_IS8;
-            }
-        }
-    }
-_End_arg:
+#if defined(TARGET_LOONGARCH64)
+    size = (uint32_t)MethodTable::GetLoongArch64PassStructInRegisterFlags(cls);
+#endif
 
     EE_TO_JIT_TRANSITION_LEAF();
 
@@ -11491,6 +11068,12 @@ void reservePersonalityRoutineSpace(uint32_t &unwindSize)
 
     // Add space for personality routine, it must be 4-byte aligned.
     unwindSize += sizeof(ULONG);
+#elif defined(TARGET_LOONGARCH64)
+    // 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
     PORTABILITY_ASSERT("reservePersonalityRoutineSpace");
 #endif // !defined(TARGET_AMD64)
@@ -11706,6 +11289,13 @@ void CEEJitInfo::allocUnwindInfo (
     ULONG * pPersonalityRoutineRW = (ULONG*)((BYTE *)pUnwindInfoRW + ALIGN_UP(unwindSize, sizeof(ULONG)));
     *pPersonalityRoutineRW = (TADDR)ProcessCLRException - baseAddress;
 
+#elif defined(TARGET_LOONGARCH64)
+
+    *(LONG *)pUnwindInfoRW |= (1 << 20); // X bit
+
+    ULONG * pPersonalityRoutineRW = (ULONG*)((BYTE *)pUnwindInfoRW + ALIGN_UP(unwindSize, sizeof(ULONG)));
+    *pPersonalityRoutineRW = ExecutionManager::GetCLRPersonalityRoutineValue();
+
 #endif
 
     EE_TO_JIT_TRANSITION();
index 87bab26..349be86 100644 (file)
@@ -394,7 +394,7 @@ extern "C"
     void STDCALL JIT_MemCpy(void *dest, const void *src, SIZE_T count);
 
     void STDMETHODCALLTYPE JIT_ProfilerEnterLeaveTailcallStub(UINT_PTR ProfilerHandle);
-#ifndef TARGET_ARM64
+#if !defined(TARGET_ARM64) && !defined(TARGET_LOONGARCH64)
     void STDCALL JIT_StackProbe();
 #endif // TARGET_ARM64
 };
diff --git a/src/coreclr/vm/loongarch64/asmconstants.h b/src/coreclr/vm/loongarch64/asmconstants.h
new file mode 100644 (file)
index 0000000..4bd74d4
--- /dev/null
@@ -0,0 +1,258 @@
+// 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
+
+// #ifndef HOST_LOONGARCH64
+// #error this file should only be used on an LOONGARCH platform
+// #endif // HOST_LOONGARCH64
+
+#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 contants 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, f12-f19.
+#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 0x58
+ASMCONSTANTS_C_ASSERT(MachState__ptrCalleeSavedRegisters == offsetof(MachState, ptrCalleeSavedRegisters))
+
+#define MachState__isValid 0xc0
+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                 32
+//#define Offset_MethodDescChunkIndex          24
+//#define Offset_PrecodeChunkIndex             25
+#define MethodDesc_ALIGNMENT_SHIFT           3
+#define FixupPrecode_ALIGNMENT_SHIFT_1       5
+
+ASMCONSTANTS_C_ASSERT(SIZEOF__FixupPrecode == sizeof(FixupPrecode));
+//ASMCONSTANTS_C_ASSERT(Offset_PrecodeChunkIndex == offsetof(FixupPrecode, m_PrecodeChunkIndex));
+//ASMCONSTANTS_C_ASSERT(Offset_MethodDescChunkIndex == offsetof(FixupPrecode, m_MethodDescChunkIndex));
+ASMCONSTANTS_C_ASSERT(MethodDesc_ALIGNMENT_SHIFT == MethodDesc::ALIGNMENT_SHIFT);
+ASMCONSTANTS_C_ASSERT((1<<FixupPrecode_ALIGNMENT_SHIFT_1) == sizeof(FixupPrecode));
+
+#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/loongarch64/asmhelpers.S b/src/coreclr/vm/loongarch64/asmhelpers.S
new file mode 100644 (file)
index 0000000..0de656c
--- /dev/null
@@ -0,0 +1,1079 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+#include "asmconstants.h"
+#include "unixasmmacros.inc"
+
+LEAF_ENTRY GetCurrentIP, _TEXT
+    ori  $a0, $ra, 0
+    jirl  $r0, $ra, 0
+LEAF_END GetCurrentIP, _TEXT
+
+// LPVOID __stdcall GetCurrentSP(void)//
+LEAF_ENTRY GetCurrentSP, _TEXT
+    ori  $a0, $sp, 0
+    jirl  $r0, $ra, 0
+LEAF_END GetCurrentSP, _TEXT
+
+//-----------------------------------------------------------------------------
+// The following Macros help in WRITE_BARRIER Implemetations
+// 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 like 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 literal pool at the end of the function
+// Placed in text section so that it is close enough to use ldr literal and still
+// be relocatable. Eliminates need for PREPARE_EXTERNAL_VAR in hot code.
+//
+// Align 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
+
+    ori  $t0, $a0, 0
+    ori  $t1, $a1, 0
+
+    la.local  $a0, g_card_table
+    ld.d  $a0, $a0, 0
+
+#ifdef FEATURE_MANUALLY_MANAGED_CARD_BUNDLES
+    la.local  $a1, g_card_bundle_table
+    ld.d  $a1, $a1, 0
+#endif
+
+#ifdef WRITE_BARRIER_CHECK
+    la.local  $a2, g_GCShadow
+    ld.d  $a2, $a2, 0
+#endif
+
+#ifdef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
+    la.local  $a3, g_sw_ww_table
+    ld.d  $a3, $a3, 0
+#endif
+
+    la.local  $a4, g_ephemeral_low
+    ld.d  $a4, $a4, 0
+
+    la.local  $a5, g_ephemeral_high
+    ld.d  $a5, $a5, 0
+
+    beq  $t0, $zero, LOCAL_LABEL(EphemeralCheckEnabled)
+
+    ori  $a4, $zero, 0
+    addi.d  $a5, $zero, -1
+LOCAL_LABEL(EphemeralCheckEnabled):
+
+    la.local  $a6, g_lowest_address
+    ld.d  $a6, $a6, 0
+
+    la.local  $a7, g_highest_address
+    ld.d  $a7, $a7, 0
+
+    // Update wbs state
+    la.local  $t2, wbs_begin   //JIT_WriteBarrier_Table_Loc
+    add.d $t2,$t2,$t1
+
+    st.d  $a0, $t2, 0
+    st.d  $a1, $t2, 8
+    st.d  $a2, $t2, 16
+    st.d  $a3, $t2, 24
+    st.d  $a4, $t2, 32
+    st.d  $a5, $t2, 40
+    st.d  $a6, $t2, 48
+    st.d  $a7, $t2, 56
+
+    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. $t0 = dst ; $t1 = val
+    ori     $t6, $a0, 0                 // $t6 = dst
+    ori     $t7, $a1, 0                 // $t7 = val
+
+    // Branch to the write barrier
+    la.local  $r21, JIT_WriteBarrier_Loc
+    ld.d  $r21, $r21, 0
+    jirl  $r0, $r21, 0
+LEAF_END JIT_WriteBarrier_Callable, _TEXT
+
+
+// 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
+    .section .data
+//.global 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
+     .previous
+
+// ------------------------------------------------------------------
+// Start of the writeable code region
+LEAF_ENTRY JIT_PatchedCodeStart, _TEXT
+    jirl  $r0, $ra, 0
+LEAF_END JIT_PatchedCodeStart, _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 regIndex, reg
+    // Incoming:
+    //
+    // $a0 = address of MachState
+    //
+    // $regIndex: Index of the register (s0-s8). For s0, index is 23.
+    //For s1, index is 24, and so on.
+    //
+    // $reg: Register name (e.g. s0, s1, etc)
+    //
+    // Get the address of the specified captured register from machine state
+    addi.d  $a2, $a0,(MachState__captureCalleeSavedRegisters + ((\regIndex-23)*8))
+
+    //// Get the content of specified preserved register pointer from machine state
+    ld.d  $a3, $a0, (MachState__ptrCalleeSavedRegisters + ((\regIndex-23)*8))
+
+    bne  $a2, $a3, LOCAL_LABEL(NoRestore_\reg)
+
+    ld.d  $\reg, $a2, 0
+LOCAL_LABEL(NoRestore_\reg):
+
+.endm
+
+// void JIT_WriteBarrier(Object** dst, Object* src)
+// On entry:
+//   t6  : the destination address (LHS of the assignment)
+//   t7  : the object reference (RHS of the assignment)
+//
+// On exit:
+//   $t0  : trashed
+//   $t1  : trashed
+//   $t3  : trashed
+//   $t4  : trashed
+//   $t6  : trashed (incremented by 8 to implement JIT_ByRefWriteBarrier contract)
+//   $t7  : trashed
+//
+WRITE_BARRIER_ENTRY JIT_WriteBarrier
+
+    dbar 0 // TODO: sync_release (runtime detection required)
+
+    st.d  $t7, $t6, 0
+
+#ifdef WRITE_BARRIER_CHECK
+    // Update GC Shadow Heap
+
+    // Do not perform the work if g_GCShadow is 0
+    la.local $t1, wbs_GCShadow
+    ld.d $t1, $t1, 0
+
+    beq  $t1, $zero, 22f //LOCAL_LABEL(ShadowUpdateDisabled)
+
+    // Compute address of shadow heap location:
+    //   pShadow = g_GCShadow + ($t6 - g_lowest_address)
+    la.local  $t3, wbs_lowest_address
+    ld.d $t3, $t3, 0
+
+    sub.d  $t3, $t6, $t3
+    add.d  $t0, $t3, $t1
+
+    // if (pShadow >= g_GCShadowEnd) goto end
+    la.local $t3, g_GCShadowEnd
+    ld.d  $t3, $t3, 0
+
+    slt  $t4, $t0, $t3
+    beq  $t4, $zero, 22f //LOCAL_LABEL(ShadowUpdateEnd)
+
+    // *pShadow = $t7
+    st.d  $t7, $t0, 0
+
+    // Ensure that the write to the shadow heap occurs before the read from the GC heap so that race
+    // conditions are caught by INVALIDGCVALUE.
+    dbar 0
+
+    // if (*$t6 == $t7) goto end
+    ld.d  $t3, $t6, 0
+    beq  $t3, $t7, 22f //LOCAL_LABEL(ShadowUpdateEnd)
+
+    // *pShadow = INVALIDGCVALUE (0xcccccccd)
+    //lu12i.w  $t3, 0xccccc
+    lu12i.w  $t3, -209716
+    ori  $t3, $t3, 0xccd
+    st.d $t3, $t0, 0
+22:
+//LOCAL_LABEL(ShadowUpdateEnd):
+//LOCAL_LABEL(ShadowUpdateDisabled):
+#endif
+
+#ifdef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
+    // Update the write watch table if necessary
+
+    la.local  $t3, wbs_sw_ww_table
+    ld.d  $t3, $t3, 0
+    beq  $t3, $zero, 1f //LOCAL_LABEL(CheckCardTable)
+
+    srli.d  $t4, $t6, 0xc
+    add.d  $t3, $t3, $t4  // SoftwareWriteWatch::AddressToTableByteIndexShift
+    ld.b  $t4, $t3, 0
+    bne  $t4, $zero, 1f //LOCAL_LABEL(CheckCardTable)
+
+    ori  $t4, $zero, 0xFF
+    st.b  $t4, $t3, 0
+1:
+//LOCAL_LABEL(CheckCardTable):
+#endif
+    // Branch to Exit if the reference is not in the Gen0 heap
+    la.local  $t3, wbs_ephemeral_low
+    ld.d  $t3, $t3, 0
+    beq  $t3, $zero, 2f //LOCAL_LABEL(SkipEphemeralCheck)
+
+    slt  $t4, $t7, $t3
+    la.local  $t3, wbs_ephemeral_high
+    ld.d  $t3, $t3, 0
+    slt  $t1, $t3, $t7
+    or  $t4, $t1, $t4
+    bne  $t4, $zero, LOCAL_LABEL(Exit)
+2:
+//LOCAL_LABEL(SkipEphemeralCheck):
+    // Check if we need to update the card table
+    la.local  $t3, wbs_card_table
+    ld.d  $t3, $t3, 0
+    srli.d  $t4, $t6, 11
+    add.d  $t7, $t3, $t4
+    ld.bu  $t1, $t7, 0
+    ori  $t4, $zero, 0xFF
+    beq  $t1, $t4, LOCAL_LABEL(Exit)
+
+    st.b  $t4, $t7, 0
+
+#ifdef FEATURE_MANUALLY_MANAGED_CARD_BUNDLES
+    // Check if we need to update the card bundle table
+    la.local  $t3, wbs_card_bundle_table
+    ld.d  $t3, $t3, 0
+    srli.d  $t4, $t6, 21
+    add.d  $t7, $t3, $t4
+
+    ld.bu  $t3, $t7, 0
+    ori  $t4, $zero, 0xFF
+    beq  $t3, $t4, LOCAL_LABEL(Exit)
+
+    st.b  $t4, $t7, 0
+#endif
+LOCAL_LABEL(Exit):
+    addi.d  $t6, $t6, 8
+    jirl  $r0, $ra, 0
+WRITE_BARRIER_END JIT_WriteBarrier
+
+NESTED_ENTRY ThePreStub, _TEXT, NoHandler
+    PROLOG_WITH_TRANSITION_BLOCK
+
+    ori  $a1, $METHODDESC_REGISTER, 0 // pMethodDesc
+
+    addi.d $a0, $sp, __PWTB_TransitionBlock        // pTransitionBlock
+    bl  PreStubWorker
+    ori  $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
+    ori  $a0, $a1, 0
+#endif
+
+    // If machine state is invalid, then simply exit
+    ld.w  $a1, $a0, MachState__isValid
+    beq  $a1, $zero, LOCAL_LABEL(Done)
+
+    RestoreRegMS  23, s0
+    RestoreRegMS  24, s1
+    RestoreRegMS  25, s2
+    RestoreRegMS  26, s3
+    RestoreRegMS  27, s4
+    RestoreRegMS  28, s5
+    RestoreRegMS  29, s6
+    RestoreRegMS  30, s7
+    RestoreRegMS  31, s8
+    RestoreRegMS  32, fp  //NOTE: here 32 is not the real fp register number.
+    RestoreRegMS  33, tp  //NOTE: here 33 is not the real tp register number.
+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.
+    ori  $v0, $zero, 0
+    jirl  $r0, $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
+    st.w  $zero, $a0, MachState__isValid
+
+    st.d  $ra, $a0, LazyMachState_captureIp
+
+    // save $sp register.
+    st.d  $sp, $a0, LazyMachState_captureSp
+
+    // save non-volatile registers that can contain object references
+    addi.d  $a1, $a0, LazyMachState_captureCalleeSavedRegisters
+    st.d  $s0, $a1, 0
+    st.d  $s1, $a1, 8
+    st.d  $s2, $a1, 16
+    st.d  $s3, $a1, 24
+    st.d  $s4, $a1, 32
+    st.d  $s5, $a1, 40
+    st.d  $s6, $a1, 48
+    st.d  $s7, $a1, 56
+    st.d  $s8, $a1, 64
+    st.d  $fp, $a1, 72
+    st.d  $tp, $a1, 80
+
+    jirl  $r0, $ra, 0
+LEAF_END LazyMachStateCaptureState, _TEXT
+
+// ------------------------------------------------------------------
+// The call in ndirect import precode points to this function.
+NESTED_ENTRY NDirectImportThunk, _TEXT, NoHandler
+
+    //                            $fp,$ra
+    PROLOG_SAVE_REG_PAIR_INDEXED  22, 1, 0xa0
+    //PROLOG_SAVE_REG gp, 16
+    SAVE_ARGUMENT_REGISTERS  $sp, 0x20
+    SAVE_FLOAT_ARGUMENT_REGISTERS  $sp, 0x60
+
+    ori  $a0, $t2, 0
+    bl C_FUNC(NDirectImportWorker)
+    ori  $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
+    //                              $fp,$ra
+    EPILOG_RESTORE_REG_PAIR_INDEXED  22, 1, 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
+
+#ifdef FEATURE_PREJIT
+//------------------------------------------------
+// VirtualMethodFixupStub
+//
+// In NGEN images, virtual slots inherited from cross-module dependencies
+// point to a jump thunk that calls into the following function that will
+// call into a VM helper. The VM helper is responsible for patching up
+// thunk, upon executing the precode, so that all subsequent calls go directly
+// to the actual method body.
+//
+// This is done lazily for performance reasons.
+//
+// On entry:
+//
+// $a0 = "this" pointer
+// $t2 = Address of thunk
+
+NESTED_ENTRY VirtualMethodFixupStub, _TEXT, NoHandler
+
+    // Save arguments and return address
+    //                            $fp,$ra
+    PROLOG_SAVE_REG_PAIR_INDEXED  22, 1, 0xa0
+    //PROLOG_SAVE_REG  gp, 16
+    SAVE_ARGUMENT_REGISTERS  $sp, 32
+    SAVE_FLOAT_ARGUMENT_REGISTERS  $sp, 96
+
+
+    // Call the helper in the VM to perform the actual fixup
+    // and tell us where to tail call. $a0 already contains
+    // the this pointer.
+
+    // Move the thunk start address in $a1
+    ori  $a1, $t2, 0
+    bl VirtualMethodFixupWorker
+    ori  $t4,$a0,0
+
+    // On return, v0 contains the target to tailcall to
+
+    // pop the stack and restore original register state
+    RESTORE_FLOAT_ARGUMENT_REGISTERS  $sp, 96
+    RESTORE_ARGUMENT_REGISTERS  $sp, 32
+    //EPILOG_RESTORE_REG  gp, 16
+    //                              $fp,$ra
+    EPILOG_RESTORE_REG_PAIR_INDEXED  22, 1, 0xa0
+
+    PATCH_LABEL  VirtualMethodFixupPatchLabel
+
+    // and tailcall to the actual method
+    EPILOG_BRANCH_REG  $t4
+NESTED_END VirtualMethodFixupStub, _TEXT
+#endif // FEATURE_PREJIT
+
+// void SinglecastDelegateInvokeStub(Delegate *pThis)
+LEAF_ENTRY SinglecastDelegateInvokeStub, _TEXT
+    beq  $a0, $zero, LOCAL_LABEL(LNullThis)
+
+    ld.d $t4, $a0, DelegateObject___methodPtr
+    ld.d $a0, $a0, DelegateObject___target
+    jirl  $r0, $t4, 0
+
+LOCAL_LABEL(LNullThis):
+    addi.d  $a0, $zero, CORINFO_NullReferenceException_ASM
+    b JIT_InternalThrow
+LEAF_END SinglecastDelegateInvokeStub, _TEXT
+
+// void JIT_ByRefWriteBarrier
+//
+// On entry:
+//   t8 : the source address (points to object reference to write)
+//   t6: the destination address (object reference written here)
+//
+// On exit:
+//   t8  : incremented by 8
+//   t7  : trashed
+//
+WRITE_BARRIER_ENTRY JIT_ByRefWriteBarrier
+    ld.d  $t7, $t8, 0
+    addi.d  $t8, $t8, 8
+    b  C_FUNC(JIT_CheckedWriteBarrier)
+WRITE_BARRIER_END JIT_ByRefWriteBarrier
+
+//-----------------------------------------------------------------------------
+// Simple WriteBarriers
+// void JIT_CheckedWriteBarrier(Object** dst, Object* src)
+//
+// On entry:
+//   t6 : the destination address (LHS of the assignment)
+//   t7 : the object reference (RHS of the assignment)
+//
+// On exit:
+//   $t1  : trashed
+//   $t0  : trashed
+//   $t3  : trashed
+//   $t4  : trashed
+//   t6  : trashed (incremented by 8 to implement JIT_ByRefWriteBarrier contract)
+//
+WRITE_BARRIER_ENTRY JIT_CheckedWriteBarrier
+
+    la.local  $t3, wbs_lowest_address
+    ld.d  $t3, $t3, 0
+    slt  $t4, $t6, $t3
+
+    la.local  $t1, wbs_highest_address
+    ld.d  $t1, $t1, 0
+    slt  $t0, $t1, $t6
+    or  $t4, $t0, $t4
+    beq  $t4, $zero, C_FUNC(JIT_WriteBarrier)
+
+    st.d  $t7, $t6, 0
+    addi.d  $t6, $t6, 8
+    jirl  $r0, $ra, 0
+WRITE_BARRIER_END JIT_CheckedWriteBarrier
+
+// ------------------------------------------------------------------
+// ThePreStubPatch()
+LEAF_ENTRY ThePreStubPatch, _TEXT
+.globl C_FUNC(ThePreStubPatchLabel)
+C_FUNC(ThePreStubPatchLabel):
+    jirl  $r0, $ra, 0
+LEAF_END ThePreStubPatch, _TEXT
+
+#ifdef FEATURE_PREJIT
+// ------------------------------------------------------------------
+// void StubDispatchFixupStub(args in regs $a0-$a7 & stack, $t1:IndirectionCellAndFlags)
+//
+// The stub dispatch thunk which transfers control to StubDispatchFixupWorker.
+NESTED_ENTRY StubDispatchFixupStub, _TEXT, NoHandler
+
+    PROLOG_WITH_TRANSITION_BLOCK
+
+    srli.d  $a1, $t8, 2
+    slli.d  $a1, $a1, 2 // Indirection cell
+
+    addi.d $a0, $sp, __PWTB_TransitionBlock        // pTransitionBlock
+    ori  $a2, $zero, 0 // sectionIndex
+    ori  $a3, $zero, 0 // pModule
+    bl StubDispatchFixupWorker
+    ori  $t4,$a0,0
+
+    EPILOG_WITH_TRANSITION_BLOCK_TAILCALL
+    PATCH_LABEL StubDispatchFixupPatchLabel
+    EPILOG_BRANCH_REG  $t4
+NESTED_END StubDispatchFixupStub, _TEXT
+#endif
+
+//
+// $t2 = UMEntryThunk*
+//
+NESTED_ENTRY TheUMEntryPrestub, _TEXT, UnhandledExceptionHandlerUnix
+
+    // Save arguments and return address
+    //                            $fp,$ra
+    PROLOG_SAVE_REG_PAIR_INDEXED  22, 1, 0xa0
+    //PROLOG_SAVE_REG  gp, 16
+    SAVE_ARGUMENT_REGISTERS  $sp, 32
+    SAVE_FLOAT_ARGUMENT_REGISTERS  $sp, 96
+
+
+    ori  $a0, $t2, 0
+    bl  TheUMEntryPrestubWorker
+    ori  $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
+    //                              $fp,$ra
+    EPILOG_RESTORE_REG_PAIR_INDEXED  22, 1, 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.d $a2, $a0, DomainLocalModule__m_pDataBlob
+    add.d $a2, $a2, $a1
+    ld.b $a2, $a2, 0
+    andi  $t8, $a2, 1
+    beq  $t8, $zero, 1f //LOCAL_LABEL(JIT_GetSharedGCStaticBase_SingleAppDomain_CallHelper)
+
+    ld.d  $v0, $a0, DomainLocalModule__m_pGCStatics
+    jirl  $r0, $ra, 0
+
+1:
+//LOCAL_LABEL(JIT_GetSharedGCStaticBase_SingleAppDomain_CallHelper):
+    // Tail call JIT_GetSharedGCStaticBase_Helper
+    bl  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
+//   t8       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
+//   $t3       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):
+//   t8       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, $t8, BACKPATCH_FLAG     // First we check if t8 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.d  $t3, $a0, 0         // retrieve the MethodTable from the object ref in $a0
+LOCAL_LABEL(MainLoop):
+    ld.d  $t1, $t1, ResolveCacheElem__pNext     // $t1 <= the next entry in the chain
+    beq  $t1, $zero, LOCAL_LABEL(Fail)
+
+    ld.d  $t4, $t1, 0
+    // compare our MT with the one in the ResolveCacheElem
+    bne  $t4, $t3, LOCAL_LABEL(MainLoop)
+
+    ld.d  $t4, $t1, 8
+    // 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, $t3
+    ld.d  $t4, $t3, 0
+    addi.d $t4, $t4, -1
+    st.d  $t4, $t3, 0
+    blt $t4, $zero, LOCAL_LABEL(Promote)
+
+    ld.d  $t4, $t1, ResolveCacheElem__target    // get the ImplTarget
+    jirl  $r0, $t4, 0                // branch to interface implemenation target
+
+LOCAL_LABEL(Promote):
+                          // Move this entry to head postion of the chain
+    addi.d  $t4, $zero, 256
+    st.d  $t4, $t3, 0         // be quick to reset the counter so we don't get a bunch of contending threads
+    ori  $t8, $t8, PROMOTE_CHAIN_FLAG   // set PROMOTE_CHAIN_FLAG
+    ori  $t2, $t1, 0           // We pass the ResolveCacheElem to ResolveWorkerAsmStub instead of the DispatchToken
+
+LOCAL_LABEL(Fail):
+    b       C_FUNC(ResolveWorkerAsmStub) // call the ResolveWorkerAsmStub method to transition into the VM
+NESTED_END ResolveWorkerChainLookupAsmStub, _TEXT
+
+
+//NOTE:  Frame_Size = SIZEOF__ArgumentRegisters + SIZEOF__FloatArgumentRegisters + extra.
+//
+//     |gp   |
+//     |s0   |
+//     |$t2   |
+//     |t9   |
+//     |$a7   |
+//     |$a6   |
+//     |$a5   |
+//     |$a4   |
+//     |$a3   |
+//     |$a2   |
+//     |$a1   |
+//     |$a0   |
+//     |$ra   | $sp+8
+//     |fp   | $sp
+//
+//     |f19  | if needed.
+//     |f18  |
+//     |f17  |
+//     |f16  |
+//     |f15  |
+//     |f14  |
+//     |f13  |
+//     |f12  |
+//
+//#define UMThunkStub_Offset_t9    0x50
+#define UMThunkStub_Offset_Entry 0x58   // offset of saved UMEntryThunk *
+#define UMThunkStub_Offset_s0    0x60
+#define UMThunkStub_StackArgs    0x70   // Frame size.
+
+
+// ------------------------------------------------------------------
+// End of the writeable code region
+LEAF_ENTRY JIT_PatchedCodeLast, _TEXT
+    jirl  $r0, $ra, 0
+LEAF_END JIT_PatchedCodeLast, _TEXT
+
+// ------------------------------------------------------------------
+// void ResolveWorkerAsmStub(args in regs $a0-$a7 & stack, t8:IndirectionCellAndFlags, $t2:DispatchToken)
+//
+// The stub dispatch thunk which transfers control to VSD_ResolveWorker.
+NESTED_ENTRY ResolveWorkerAsmStub, _TEXT, NoHandler
+
+    PROLOG_WITH_TRANSITION_BLOCK
+
+    ori  $a2, $t2, 0                 // DispatchToken
+    addi.d  $a0, $sp, __PWTB_TransitionBlock        // pTransitionBlock
+    srli.d  $a1, $t8, 2
+    andi  $a3, $t8, 3              // flag
+    slli.d  $a1, $a1, 2
+    bl C_FUNC(VSD_ResolveWorker)
+    ori  $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
+    jirl  $r0, $ra, 0
+LEAF_END JIT_GetSharedNonGCStaticBaseNoCtor_SingleAppDomain, _TEXT
+
+// ------------------------------------------------------------------
+// void* JIT_GetSharedGCStaticBaseNoCtor(SIZE_T moduleDomainID, DWORD dwClassDomainID)
+
+LEAF_ENTRY JIT_GetSharedGCStaticBaseNoCtor_SingleAppDomain, _TEXT
+    ld.d  $a0, $a0, DomainLocalModule__m_pGCStatics
+    jirl  $r0, $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
+    //                             $fp,$ra
+    PROLOG_SAVE_REG_PAIR_INDEXED   22, 1, 0x80
+
+    // Spill callee saved registers
+    //                    $s0,$s1
+    PROLOG_SAVE_REG_PAIR   23, 24, 16
+    PROLOG_SAVE_REG_PAIR   25, 26, 32
+    PROLOG_SAVE_REG_PAIR   27, 28, 48
+    PROLOG_SAVE_REG_PAIR   29, 30, 64
+    PROLOG_SAVE_REG 31, 80
+
+    // save any integral return value(s)
+    st.d  $v0, $sp, 96
+    st.d  $v1, $sp, 104
+
+    // save any FP/HFA return value(s)
+    fst.d  $f0, $sp, 112
+    fst.d  $f1, $sp, 120
+
+    ori  $a0, $sp, 0
+    bl C_FUNC(OnHijackWorker)
+
+    // restore callee saved registers
+
+    // restore any integral return value(s)
+    ld.d  $v0, $sp, 96
+    ld.d  $v1, $sp, 104
+
+    // restore any FP/HFA return value(s)
+    fst.d  $f0, $sp, 112
+    fst.d  $f1, $sp, 120
+
+    EPILOG_RESTORE_REG_PAIR   23, 24, 16
+    EPILOG_RESTORE_REG_PAIR   25, 26, 32
+    EPILOG_RESTORE_REG_PAIR   27, 28, 48
+    EPILOG_RESTORE_REG_PAIR   29, 30, 64
+    EPILOG_RESTORE_REG  31, 80
+    //                              $fp,$ra
+    EPILOG_RESTORE_REG_PAIR_INDEXED  22, 1, 0x80
+    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.
+    //
+
+    //                            $fp,$ra
+    PROLOG_SAVE_REG_PAIR_INDEXED   22, 1, 96, 0
+
+    // Spill callee saved registers
+    //                    $s0,$s1
+    PROLOG_SAVE_REG_PAIR   23, 24, 16
+    PROLOG_SAVE_REG_PAIR   25, 26, 32
+    PROLOG_SAVE_REG_PAIR   27, 28, 48
+    PROLOG_SAVE_REG_PAIR   29, 30, 64
+    //                    $s8,$tp
+    PROLOG_SAVE_REG_PAIR   31, 2,  80
+    //PROLOG_SAVE_REG 31, 80
+
+    // Save the SP of this function
+    st.d  $sp, $a3, 0
+
+    ld.d  $tp, $a2, -168  // offset of tp in PCONTEXT relative to S0.
+    ld.d  $fp, $a2, -8  // offset of fp in PCONTEXT relative to S0.
+    ld.d  $s0, $a2, 0
+    ld.d  $s1, $a2, 8
+    ld.d  $s2, $a2, 16
+    ld.d  $s3, $a2, 24
+    ld.d  $s4, $a2, 32
+    ld.d  $s5, $a2, 40
+    ld.d  $s6, $a2, 48
+    ld.d  $s7, $a2, 56
+    ld.d  $s8, $a2, 64  // offset of fp in PCONTEXT relative to S0.
+
+    // Invoke the funclet
+    jirl $ra, $a1, 0
+
+    EPILOG_RESTORE_REG_PAIR   23, 24, 16
+    EPILOG_RESTORE_REG_PAIR   25, 26, 32
+    EPILOG_RESTORE_REG_PAIR   27, 28, 48
+    EPILOG_RESTORE_REG_PAIR   29, 30, 64
+    EPILOG_RESTORE_REG_PAIR   31,  2, 80
+    //EPILOG_RESTORE_REG  31, 80
+    //                              $fp,$ra
+    EPILOG_RESTORE_REG_PAIR_INDEXED   22, 1, 96
+    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
+    //                             $fp,$ra
+    PROLOG_SAVE_REG_PAIR_INDEXED   22, 1, 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
+    st.d $fp, $a3, 0
+    // Invoke the filter funclet
+    jirl  $ra, $a2, 0
+
+    EPILOG_RESTORE_REG_PAIR_INDEXED   22, 1, 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
+    fst.d  $f0, $a1, 0
+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).
+//
+// On entry:
+//   $a0          : size of the FP result (4 or 8 bytes)
+//   $a1          : 32-bit or 64-bit FP result
+//
+// On exit:
+//   f0          : float result if x0 == 4
+//   f0          : double result if x0 == 8
+//
+LEAF_ENTRY setFPReturn, _TEXT
+    movgr2fr.d  $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.d  $a2, $a0, DomainLocalModule__m_pDataBlob
+
+    ldx.b $a2, $a2, $a1
+    andi  $t4, $a2, 1
+    beq  $t4, $zero, LOCAL_LABEL(JIT_GetSharedNonGCStaticBase_SingleAppDomain_CallHelper)
+
+    jirl  $r0, $ra, 0
+
+LOCAL_LABEL(JIT_GetSharedNonGCStaticBase_SingleAppDomain_CallHelper):
+    // Tail call JIT_GetSharedNonGCStaticBase_Helper
+    b  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
+
+    ori $a1, $t8, 0      // Indirection cell
+    ori $a2, $t0, 0      // sectionIndex
+    ori $a3, $t1, 0      // Module*
+
+    addi.d  $a0, $sp, __PWTB_TransitionBlock        // pTransitionBlock
+    bl C_FUNC(ExternalMethodFixupWorker)
+    ori  $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)
+    ori  $a1, $t8, 0      // Indirection cell
+    ori  $a2, $t0, 0      // sectionIndex
+    ori  $a3, $t1, 0      // Module*
+    ori  $a4, $r0, \frameFlags
+
+    addi.d  $a0, $sp, __PWTB_TransitionBlock        // pTransitionBlock
+    bl  DynamicHelperWorker
+
+    bne $v0, $r0, LOCAL_LABEL(FakeProlog\suffix\()_0)
+
+    ld.d  $v0, $sp, __PWTB_ArgumentRegisters
+    EPILOG_WITH_TRANSITION_BLOCK_RETURN
+LOCAL_LABEL(FakeProlog\suffix\()_0):
+    ori  $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
+    jirl  $r0, $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
+    //   $t3 = throwable
+    //
+    // On exit:
+    //   Values of $a0-$a7, $fa0-$fa7, $fp are preserved.
+    //   Values of other volatile registers are not preserved.
+
+    //                           $fp,$ra
+    PROLOG_SAVE_REG_PAIR_INDEXED 22, 1, SIZEOF__PROFILE_PLATFORM_SPECIFIC_DATA // Allocate space and save Fp, Pc.
+    SAVE_ARGUMENT_REGISTERS $sp, 16          // Save $t0 and argument registers ($a0-$a7).
+    st.d     $zero, $sp, 88                   // Clear functionId.
+    SAVE_FLOAT_ARGUMENT_REGISTERS $sp, 96    // Save floating-point/SIMD registers ($fa0-$fa7).
+    addi.d     $t3, $fp, SIZEOF__PROFILE_PLATFORM_SPECIFIC_DATA // Compute probeSp - initial value of Sp on entry to the helper.
+    st.d     $t3, $sp, 224             // Save probeSp.
+    st.d     $t2, $sp, 232             // Save profiledSp.
+
+    st.d    $zero, $sp, 240            // Clear hiddenArg.
+    addi.d  $t3, $zero, \flags
+    st.w    $t3, $sp, 248              // Save flags.
+    st.d    $zero, $sp, 256            // clear unused field.
+
+    ori     $a1, $t1, 0
+    ori     $a2, $sp, 0
+    bl      C_FUNC(\helper)
+
+    RESTORE_ARGUMENT_REGISTERS $sp, 16       // Restore $t0 and argument registers.
+    RESTORE_FLOAT_ARGUMENT_REGISTERS $sp, 96 // Restore floating-point/SIMD registers.
+    //                              $fp, $ra
+    EPILOG_RESTORE_REG_PAIR_INDEXED 22, 1, 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.d     $a0, $sp, __PWTB_TransitionBlock // TransitionBlock *
+    ori     $a1, $t1, 0 // stub-identifying token
+    bl      C_FUNC(OnCallCountThresholdReached)
+    ori     $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/loongarch64/calldescrworkerloongarch64.S b/src/coreclr/vm/loongarch64/calldescrworkerloongarch64.S
new file mode 100644 (file)
index 0000000..66373c2
--- /dev/null
@@ -0,0 +1,208 @@
+// 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
+    //                           $fp,$ra
+    PROLOG_SAVE_REG_PAIR_INDEXED  22, 1, 0x20
+    //             $s0=23.
+    PROLOG_SAVE_REG 23, 16
+    ld.wu  $a1, $a0, CallDescrData__numStackSlots
+
+    ori  $s0, $a0, 0 // save pCallDescrData in s0
+    beq  $a1, $zero, LOCAL_LABEL(donestack)
+
+    slli.d  $a2, $a1, 3
+    andi  $a0, $a2, 0x8
+    sub.d  $t4, $sp, $a0   //padding on high-addr.
+    add.d  $a0, $a0, $a2
+    sub.d  $sp, $sp, $a0   //stack-16byte aligned.
+
+    ld.d  $a0, $s0, CallDescrData__pSrc
+
+    add.d  $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.d  $a2, $a2, -8
+    ld.d  $a4, $a2, 0
+    addi.d  $t4, $t4, -8
+    st.d  $a4, $t4, 0
+    addi.d    $a1, $a1, -1
+    bne $a1, $zero, LOCAL_LABEL(stackloop)
+
+LOCAL_LABEL(donestack):
+    // If FP arguments are supplied in registers ($t4 != NULL)
+    ld.d  $t4, $s0, CallDescrData__pFloatArgumentRegisters
+    beq  $t4, $zero, LOCAL_LABEL(NoFloatingPoint)
+
+    fld.d  $fa0, $t4, 0
+    fld.d  $fa1, $t4, 8
+    fld.d  $fa2, $t4, 16
+    fld.d  $fa3, $t4, 24
+    fld.d  $fa4, $t4, 32
+    fld.d  $fa5, $t4, 40
+    fld.d  $fa6, $t4, 48
+    fld.d  $fa7, $t4, 56
+LOCAL_LABEL(NoFloatingPoint):
+
+    // Copy [pArgumentRegisters, ..., pArgumentRegisters + 56]
+    // into $a0, ..., a7
+
+    ld.d  $t4, $s0, CallDescrData__pArgumentRegisters
+    ld.d  $a0, $t4, 0
+    ld.d  $a1, $t4, 8
+    ld.d  $a2, $t4, 16
+    ld.d  $a3, $t4, 24
+    ld.d  $a4, $t4, 32
+    ld.d  $a5, $t4, 40
+    ld.d  $a6, $t4, 48
+    ld.d  $a7, $t4, 56
+
+    ld.d  $t4, $s0, CallDescrData__pTarget
+
+    // call pTarget
+    jirl $ra, $t4, 0
+
+    ld.w  $a3, $s0, CallDescrData__fpReturnSize
+
+    // 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)
+
+    //b  LOCAL_LABEL(NotCorrectReturn)
+LOCAL_LABEL(NotCorrectReturn):
+    st.w  $ra, $zero, 0
+    EMIT_BREAKPOINT // Unreachable
+
+LOCAL_LABEL(FloatReturn):
+    fst.s  $f0, $s0, CallDescrData__returnValue
+    b  LOCAL_LABEL(ReturnDone)
+
+LOCAL_LABEL(DoubleReturn):
+    fst.d  $f0, $s0, CallDescrData__returnValue
+    b  LOCAL_LABEL(ReturnDone)
+
+LOCAL_LABEL(FloatIntReturn):
+    fst.s  $f0, $s0, CallDescrData__returnValue
+    st.w  $a0, $s0, CallDescrData__returnValue + 4
+    b  LOCAL_LABEL(ReturnDone)
+
+LOCAL_LABEL(DoubleIntReturn):
+    fst.d  $f0, $s0, CallDescrData__returnValue
+    st.w  $a0, $s0, CallDescrData__returnValue + 8
+    b  LOCAL_LABEL(ReturnDone)
+
+LOCAL_LABEL(FloatLongReturn):
+    fst.s  $f0, $s0, CallDescrData__returnValue
+    st.d  $a0, $s0, CallDescrData__returnValue + 8
+    b  LOCAL_LABEL(ReturnDone)
+
+LOCAL_LABEL(DoubleLongReturn):
+    fst.d  $f0, $s0, CallDescrData__returnValue
+    st.d  $a0, $s0, CallDescrData__returnValue + 8
+    b  LOCAL_LABEL(ReturnDone)
+
+LOCAL_LABEL(IntFloatReturn):
+    st.w  $a0, $s0, CallDescrData__returnValue
+    fst.s  $f0, $s0, CallDescrData__returnValue + 4
+    b  LOCAL_LABEL(ReturnDone)
+
+LOCAL_LABEL(LongFloatReturn):
+    st.d  $a0, $s0, CallDescrData__returnValue
+    fst.s  $f0, $s0, CallDescrData__returnValue + 8
+    b  LOCAL_LABEL(ReturnDone)
+
+LOCAL_LABEL(IntDoubleReturn):
+    st.w  $a0, $s0, CallDescrData__returnValue
+    fst.d  $f0, $s0, CallDescrData__returnValue + 8
+    b  LOCAL_LABEL(ReturnDone)
+
+LOCAL_LABEL(LongDoubleReturn):
+    st.d  $a0, $s0, CallDescrData__returnValue
+    fst.d  $f0, $s0, CallDescrData__returnValue + 8
+    b  LOCAL_LABEL(ReturnDone)
+
+LOCAL_LABEL(FloatFloatReturn):
+    fst.s  $f0, $s0, CallDescrData__returnValue
+    fst.s  $f1, $s0, CallDescrData__returnValue + 4
+    b  LOCAL_LABEL(ReturnDone)
+
+LOCAL_LABEL(DoubleFloatReturn):
+    fst.d  $f0, $s0, CallDescrData__returnValue
+    fst.s  $f1, $s0, CallDescrData__returnValue + 8
+    b  LOCAL_LABEL(ReturnDone)
+
+LOCAL_LABEL(FloatDoubleReturn):
+    fst.s  $f0, $s0, CallDescrData__returnValue
+    fst.d  $f1, $s0, CallDescrData__returnValue + 8
+    b  LOCAL_LABEL(ReturnDone)
+
+LOCAL_LABEL(DoubleDoubleReturn):
+    fst.d  $f0, $s0, CallDescrData__returnValue
+    fst.d  $f1, $s0, CallDescrData__returnValue + 8
+    b  LOCAL_LABEL(ReturnDone)
+
+LOCAL_LABEL(IntReturn):
+    // Save return value into retbuf for int
+    st.d  $a0, $s0, CallDescrData__returnValue
+    st.d  $a1, $s0, CallDescrData__returnValue + 8
+
+LOCAL_LABEL(ReturnDone):
+
+    EPILOG_STACK_RESTORE
+    EPILOG_RESTORE_REG  23, 16
+    EPILOG_RESTORE_REG_PAIR_INDEXED  22, 1, 0x20
+    jirl  $r0, $ra, 0
+NESTED_END CallDescrWorkerInternal, _TEXT
diff --git a/src/coreclr/vm/loongarch64/cgencpu.h b/src/coreclr/vm/loongarch64/cgencpu.h
new file mode 100644 (file)
index 0000000..2777242
--- /dev/null
@@ -0,0 +1,511 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+#ifndef TARGET_LOONGARCH64
+#error Should only include "cgencpu.h" for LOONGARCH64 builds
+#endif
+
+#ifndef __cgencpu_h__
+#define __cgencpu_h__
+
+#define INSTRFMT_K64
+#include <stublink.h>
+
+#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                      40  // # bytes to allocate for a jump instruction
+#define BACK_TO_BACK_JUMP_ALLOCATE_SIZE         40  // # 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
+#define HAS_FIXUP_PRECODE_CHUNKS                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: f0 and f1)
+#define ENREGISTERED_RETURNTYPE_INTEGER_MAXSIZE 16  // bytes (two int registers: v0 and v1)
+#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 FLOAT_REGISTER_SIZE 16 // each register in FloatArgumentRegisters is 16 bytes. Loongarch size is ??
+
+// 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 8
+
+//**********************************************************************
+// 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) registers saved as
+// of a FramedMethodFrame.
+//--------------------------------------------------------------------
+typedef DPTR(struct CalleeSavedRegisters) PTR_CalleeSavedRegisters;
+struct CalleeSavedRegisters {
+    INT64 fp; // frame pointer.
+    INT64 ra; // return register
+    INT64 s0;
+    INT64 s1;
+    INT64 s2;
+    INT64 s3;
+    INT64 s4;
+    INT64 s5;
+    INT64 s6;
+    INT64 s7;
+    INT64 s8;
+    INT64 tp;
+};
+
+//--------------------------------------------------------------------
+// This represents the arguments that are stored in volatile 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.
+//--------------------------------------------------------------------
+typedef DPTR(struct ArgumentRegisters) PTR_ArgumentRegisters;
+struct ArgumentRegisters {
+    INT64 a[8]; // a0 ....a7
+};
+#define NUM_ARGUMENT_REGISTERS 8
+
+#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.
+//--------------------------------------------------------------------
+typedef DPTR(struct FloatArgumentRegisters) PTR_FloatArgumentRegisters;
+struct FloatArgumentRegisters {
+    //TODO: not supports LOONGARCH-SIMD.
+    double  f[8];  // f0-f7
+};
+#define NUM_FLOAT_ARGUMENT_REGISTERS 8
+
+
+//**********************************************************************
+// 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;
+}
+
+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
+    {
+        _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)
+{
+    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);
+
+    // pcaddi  $r21,4
+    // ld.d  $r21,$r21,0
+    // jirl  $r0,$r21,0
+    // nop    //padding.
+
+    pCode[0] = 0x18000095; //pcaddi  $r21,4
+    pCode[1] = 0x28c002b5; //ld.d  $r21,$r21,0
+    pCode[2] = 0x4c0002a0; //jirl  $r0,$r21,0
+    pCode[3] = 0x03400000; //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<PTR_PCODE>(pInstr + 16);
+}
+
+//------------------------------------------------------------------------
+inline BOOL isJump(PCODE pCode)
+{
+    LIMITED_METHOD_DAC_CONTRACT;
+
+    TADDR pInstr = PCODEToPINSTR(pCode);
+
+    return *dac_cast<PTR_DWORD>(pInstr+12) == 0x03400000; //nop
+}
+
+//------------------------------------------------------------------------
+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 VecReg
+{
+    int reg;
+    VecReg(int reg):reg(reg)
+    {
+        _ASSERTE(0 <= reg && reg < 32);
+    }
+
+    operator int() { return reg; }
+    int operator == (VecReg other) { return reg == other.reg; }
+    int operator != (VecReg other) { return reg != other.reg; }
+    WORD Mask() const { return 1 << reg; }
+};
+
+const IntReg RegSp  = IntReg(3);
+const IntReg RegFp  = IntReg(22);
+const IntReg RegRa  = IntReg(1);
+
+#define GetEEFuncEntryPoint(pfn) GFN_TADDR(pfn)
+
+class StubLinkerCPU : public StubLinker
+{
+
+private:
+    void EmitLoadStoreRegPairImm(DWORD flags, int regNum1, int regNum2, IntReg Rn, int offset, BOOL isVec);
+    void EmitLoadStoreRegImm(DWORD flags, int regNum, IntReg Rn, int offset, BOOL isVec, int log2Size = 3);
+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 );
+    }
+    static int emitIns_O_R_R_I(int op, int rd, int rj, int imm) {
+        _ASSERTE(isValidSimm12(imm));
+        _ASSERTE(!(rd >> 5));
+        _ASSERTE(!(rj >> 5));
+        _ASSERTE(op < 0x400);
+        return ((op & 0x3ff)<<22) | ((rj & 0x1f)<<5) | (rd & 0x1f) | ((imm & 0xfff)<<10);
+    }
+
+    void EmitCallManagedMethod(MethodDesc *pMD, BOOL fTailCall);
+    void EmitCallLabel(CodeLabel *target, BOOL fTailCall, BOOL fIndirect);
+
+    void EmitShuffleThunk(struct ShuffleEntry *pShuffleEntryArray);
+
+    void EmitNop() { Emit32(0x03400000); }
+    void EmitBreakPoint() { Emit32(0x002a0000); }
+
+#if defined(FEATURE_SHARE_GENERIC_CODE)
+    void EmitComputedInstantiatingMethodStub(MethodDesc* pSharedMD, struct ShuffleEntry *pShuffleEntryArray, void* extraArg);
+#endif // FEATURE_SHARE_GENERIC_CODE
+
+    void EmitMovConstant(IntReg Rd, UINT64 constant);
+    void EmitJumpRegister(IntReg regTarget);
+    void EmitMovReg(IntReg dest, IntReg source);
+    void EmitMovFloatReg(FloatReg Fd, FloatReg Fs);
+
+    void EmitSubImm(IntReg Rd, IntReg Rn, unsigned int value);
+    void EmitAddImm(IntReg Rd, IntReg Rn, unsigned int value);
+
+    void EmitLoadStoreRegPairImm(DWORD flags, IntReg Rt1, IntReg Rt2, IntReg Rn, int offset=0);
+    void EmitLoadStoreRegPairImm(DWORD flags, VecReg Vt1, VecReg Vt2, IntReg Xn, int offset=0);
+
+    void EmitLoadStoreRegImm(DWORD flags, IntReg Rt, IntReg Rn, int offset=0, int log2Size = 3);
+
+#if defined(TARGET_LOONGARCH64)
+    void EmitFloatLoadStoreRegImm(DWORD flags, FloatReg Ft, IntReg Xn, int offset=0);
+#else
+    void EmitLoadStoreRegImm(DWORD flags, VecReg Vt, IntReg Xn, int offset=0);
+#endif
+    void EmitLoadFloatRegImm(FloatReg ft, IntReg base, int offset);
+};
+
+extern "C" void SinglecastDelegateInvokeStub();
+
+
+// preferred alignment for data
+#define DATA_ALIGNMENT 8
+
+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 V0;
+             DWORD64 V1;
+         };
+        size_t ReturnValue[2];
+    };
+    union
+    {
+        struct {
+             DWORD64 F0;
+             DWORD64 F1;
+         };
+        size_t FPReturnValue[2];
+    };
+    DWORD64 S0, S1, S2, S3, S4, S5, S6, S7, S8, Tp;
+    DWORD64 Fp; // frame pointer
+    union
+    {
+        DWORD64 Ra;
+        size_t ReturnAddress;
+    };
+};
+
+EXTERN_C VOID STDCALL PrecodeFixupThunk();
+
+// Precode to shuffle this and retbuf for closed delegates over static methods with return buffer
+struct ThisPtrRetBufPrecode {
+
+    static const int Type = 2;//2, for Type encoding.
+
+    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<ThisPtrRetBufPrecode> 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/loongarch64/crthelpers.S b/src/coreclr/vm/loongarch64/crthelpers.S
new file mode 100644 (file)
index 0000000..88fd219
--- /dev/null
@@ -0,0 +1,37 @@
+// 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"
+
+// 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)
+
+    ld.b  $zero, $a0, 0     //Is this really needed ?
+
+    b  memset
+
+LOCAL_LABEL(JIT_MemSet_ret):
+    jirl  $r0, $ra, 0
+
+////NOTO: Here must use LEAF_END_MARKED!  not LEAF_END !!!
+LEAF_END_MARKED JIT_MemSet, _TEXT
+
+LEAF_ENTRY JIT_MemCpy, _TEXT
+    beq $a2, $zero, LOCAL_LABEL(JIT_MemCpy_ret)
+
+    ld.b  $zero, $a0, 0
+    ld.b  $zero, $a1, 0    //Is this really needed ?
+
+    b  memcpy
+
+LOCAL_LABEL(JIT_MemCpy_ret):
+    jirl  $r0, $ra, 0
+
+////NOTO: Here must use LEAF_END_MARKED!  not LEAF_END !!!
+LEAF_END_MARKED JIT_MemCpy, _TEXT
diff --git a/src/coreclr/vm/loongarch64/excepcpu.h b/src/coreclr/vm/loongarch64/excepcpu.h
new file mode 100644 (file)
index 0000000..14a8fd1
--- /dev/null
@@ -0,0 +1,46 @@
+// 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__
+
+
+#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 LOONGARCH, 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 or NakedThrowHelper.
+//
+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/loongarch64/gmscpu.h b/src/coreclr/vm/loongarch64/gmscpu.h
new file mode 100644 (file)
index 0000000..01420a8
--- /dev/null
@@ -0,0 +1,102 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+/**************************************************************/
+/*                       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__
+
+// S0 - S8, FP, TP
+#define NUM_CALLEESAVED_REGISTERS 11
+
+struct MachState {
+    ULONG64        captureCalleeSavedRegisters[NUM_CALLEESAVED_REGISTERS]; // preserved registers
+    PTR_ULONG64    ptrCalleeSavedRegisters[NUM_CALLEESAVED_REGISTERS]; // 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 *)&copy->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_CALLEESAVED_REGISTERS; 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/loongarch64/pinvokestubs.S b/src/coreclr/vm/loongarch64/pinvokestubs.S
new file mode 100644 (file)
index 0000000..48a4ec9
--- /dev/null
@@ -0,0 +1,193 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+#include "asmconstants.h"
+#include "unixasmmacros.inc"
+
+// ------------------------------------------------------------------
+// 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.d   $t0, \VASigCookieReg, VASigCookie__pNDirectILStub
+
+        // 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.d    \HiddenArg, \HiddenArg, 1
+            ori       \HiddenArg, \HiddenArg, 1
+        .endif
+
+        jirl  $r0, $t0, 0
+
+        NESTED_END \__PInvokeStubFuncName, _TEXT
+
+        NESTED_ENTRY \__PInvokeGenStubFuncName, _TEXT, NoHandler
+
+        PROLOG_WITH_TRANSITION_BLOCK 0, 0, \SaveFPArgs
+
+        // $a2 = Umanaged Target\MethodDesc
+        ori  $a2, \HiddenArg, 0
+
+        // $a1 = VaSigCookie
+        ori  $a1, \VASigCookieReg, 0
+
+        // $a0 = pTransitionBlock
+        addi.d  $a0, $sp, __PWTB_TransitionBlock
+
+        // save hidden arg
+        ori  $s0, \HiddenArg, 0
+
+        // save VASigCookieReg
+        ori  $s1, \VASigCookieReg, 0
+
+        bl  C_FUNC(\__PInvokeStubWorkerName)
+
+        // restore VASigCookieReg
+        ori  \VASigCookieReg, $s1, 0
+
+        // restore hidden arg (method desc or unmanaged target)
+        ori  \HiddenArg, $s0, 0
+
+        EPILOG_WITH_TRANSITION_BLOCK_TAILCALL
+
+        EPILOG_BRANCH       C_FUNC(\__PInvokeStubFuncName)
+
+        NESTED_END \__PInvokeGenStubFuncName, _TEXT
+.endm
+
+// ------------------------------------------------------------------
+// IN:
+// InlinedCallFrame ($a0) = pointer to the InlinedCallFrame data, including the GS cookie slot (GS cookie right
+//                         before actual InlinedCallFrame data)
+//
+//
+NESTED_ENTRY JIT_PInvokeBegin, _TEXT, NoHandler
+
+    //                            $fp,$ra
+    PROLOG_SAVE_REG_PAIR_INDEXED   22, 1, 32
+    //                $s0=23.
+    PROLOG_SAVE_REG    23, 16           //the stack slot at $sp+24 is empty for 16 byte alignment
+
+    PREPARE_EXTERNAL_VAR  s_gsCookie, $t0
+    ld.d  $t4, $t0, 0
+    st.d  $t4, $a0, 0
+    addi.d  $s0, $a0, SIZEOF__GSCookie
+
+    // s0 = pFrame
+    // set first slot to the value of InlinedCallFrame::`vftable' (checked by runtime code)
+    PREPARE_EXTERNAL_VAR  _ZTV16InlinedCallFrame, $t0
+    addi.d  $t4, $t0, 16
+    st.d  $t4, $s0, 0
+
+    st.d  $zero, $s0, InlinedCallFrame__m_Datum
+
+    addi.d  $t0, $sp, 32
+    st.d  $t0, $s0, InlinedCallFrame__m_pCallSiteSP
+    st.d  $ra, $s0, InlinedCallFrame__m_pCallerReturnAddress
+
+    ld.d  $t4, $sp, 0
+    st.d  $t4, $s0, InlinedCallFrame__m_pCalleeSavedFP
+
+    // v0 = GetThread()
+    bl  GetThreadHelper
+
+    st.d  $v0, $s0, InlinedCallFrame__m_pThread
+
+    // pFrame->m_Next = pThread->m_pFrame;
+    ld.d  $t4, $v0, Thread_m_pFrame
+    st.d  $t4, $s0, Frame__m_Next
+
+    // pThread->m_pFrame = pFrame;
+    st.d  $s0, $v0, Thread_m_pFrame
+
+    // pThread->m_fPreemptiveGCDisabled = 0
+    st.w     $zero, $v0, Thread_m_fPreemptiveGCDisabled
+
+    EPILOG_RESTORE_REG      23, 16    //the stack slot at $sp+24 is empty for 16 byte alignment
+    EPILOG_RESTORE_REG_PAIR_INDEXED 22, 1, 32
+    EPILOG_RETURN
+
+NESTED_END JIT_PInvokeBegin, _TEXT
+
+// ------------------------------------------------------------------
+// IN:
+// InlinedCallFrame ($a0) = pointer to the InlinedCallFrame data, including the GS cookie slot (GS cookie right
+//                         before actual InlinedCallFrame data)
+//
+//
+LEAF_ENTRY JIT_PInvokeEnd, _TEXT
+
+    addi.d  $a0, $a0, SIZEOF__GSCookie
+    ld.d      $a1, $a0, InlinedCallFrame__m_pThread
+    // $a0 = pFrame
+    // $a1 = pThread
+
+    // pThread->m_fPreemptiveGCDisabled = 1
+    ori  $t4, $r0, 1
+    st.w   $t4, $a1, Thread_m_fPreemptiveGCDisabled
+
+    // Check return trap
+    PREPARE_EXTERNAL_VAR  g_TrapReturningThreads, $t0
+    ld.w  $t4, $t0, 0
+    bne  $t4, $zero, LOCAL_LABEL(RarePath)
+
+    // pThread->m_pFrame = pFrame->m_Next
+    ld.d  $t4, $a0, Frame__m_Next
+    st.d  $t4, $a1, Thread_m_pFrame
+
+    jirl  $r0, $ra, 0
+
+LOCAL_LABEL(RarePath):
+    b  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/loongarch64/profiler.cpp b/src/coreclr/vm/loongarch64/profiler.cpp
new file mode 100644 (file)
index 0000000..4668e7b
--- /dev/null
@@ -0,0 +1,303 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+#include "common.h"
+
+#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)
+{
+    LIMITED_METHOD_CONTRACT;
+
+    PROFILE_PLATFORM_SPECIFIC_DATA* pData = reinterpret_cast<PROFILE_PLATFORM_SPECIFIC_DATA*>(pPlatformSpecificHandle);
+    return (UINT_PTR)pData->Pc;
+}
+
+void ProfileSetFunctionIDInPlatformSpecificHandle(void* pPlatformSpecificHandle, FunctionID functionId)
+{
+    LIMITED_METHOD_CONTRACT;
+
+    _ASSERTE(pPlatformSpecificHandle != nullptr);
+    _ASSERTE(functionId != 0);
+
+    PROFILE_PLATFORM_SPECIFIC_DATA* pData = reinterpret_cast<PROFILE_PLATFORM_SPECIFIC_DATA*>(pPlatformSpecificHandle);
+    pData->functionId = functionId;
+}
+
+ProfileArgIterator::ProfileArgIterator(MetaSig* pSig, void* pPlatformSpecificHandle)
+    : m_argIterator(pSig)
+{
+    WRAPPER_NO_CONTRACT;
+
+    _ASSERTE(pSig != nullptr);
+    _ASSERTE(pPlatformSpecificHandle != nullptr);
+
+    m_handle = pPlatformSpecificHandle;
+
+    PROFILE_PLATFORM_SPECIFIC_DATA* pData = reinterpret_cast<PROFILE_PLATFORM_SPECIFIC_DATA*>(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()
+{
+    LIMITED_METHOD_CONTRACT;
+
+    m_handle = nullptr;
+}
+
+LPVOID ProfileArgIterator::GetNextArgAddr()
+{
+    WRAPPER_NO_CONTRACT;
+
+    _ASSERTE(m_handle != nullptr);
+
+    PROFILE_PLATFORM_SPECIFIC_DATA* pData = reinterpret_cast<PROFILE_PLATFORM_SPECIFIC_DATA*>(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)
+{
+    LIMITED_METHOD_CONTRACT;
+
+    PROFILE_PLATFORM_SPECIFIC_DATA* pData = reinterpret_cast<PROFILE_PLATFORM_SPECIFIC_DATA*>(m_handle);
+
+    return pData->hiddenArg;
+}
+
+LPVOID ProfileArgIterator::GetThis(void)
+{
+    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)
+{
+    CONTRACTL
+    {
+        NOTHROW;
+        GC_NOTRIGGER;
+    }
+    CONTRACTL_END;
+
+    PROFILE_PLATFORM_SPECIFIC_DATA* pData = reinterpret_cast<PROFILE_PLATFORM_SPECIFIC_DATA*>(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 LOONGARCH 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/loongarch64/stubs.cpp b/src/coreclr/vm/loongarch64/stubs.cpp
new file mode 100644 (file)
index 0000000..1d2ca89
--- /dev/null
@@ -0,0 +1,1966 @@
+// 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 LOONGARCH64 platform.
+
+#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 B.cond
+//-----------------------------------------------------------------------
+class ConditionalBranchInstructionFormat : public InstructionFormat
+{
+
+    public:
+        ConditionalBranchInstructionFormat() : InstructionFormat(InstructionFormat::k32)
+        {
+            LIMITED_METHOD_CONTRACT;
+        }
+
+        virtual UINT GetSizeOfInstruction(UINT refsize, UINT variationCode)
+        {
+            LIMITED_METHOD_CONTRACT;
+
+            _ASSERTE(!"LOONGARCH64: not implementation on loongarch64!!!");
+            _ASSERTE(refsize == InstructionFormat::k32);
+
+            return 4;
+        }
+
+        virtual UINT GetHotSpotOffset(UINT refsize, UINT variationCode)
+        {
+            WRAPPER_NO_CONTRACT;
+            return 0;
+        }
+
+
+        virtual BOOL CanReach(UINT refSize, UINT variationCode, BOOL fExternal, INT_PTR offset)
+        {
+            _ASSERTE(!fExternal || "LOONGARCH64:NYI - CompareAndBranchInstructionFormat::CanReach external");
+            if (fExternal)
+                return false;
+
+            if (offset < -1048576 || offset > 1048572)
+                return false;
+            return true;
+        }
+        ////TODO: add for LOONGARCH. unused now!
+        // B.<cond> <label>
+        // Encoding 0|1|0|1|0|1|0|0|imm19|0|cond
+        // cond = Bits3-0(variation)
+        // imm19 = bits19-0(fixedUpReference/4), will be SignExtended
+        virtual VOID EmitInstruction(UINT refSize, __int64 fixedUpReference, BYTE *pOutBufferRX, BYTE *pOutBufferRW, UINT variationCode, BYTE *pDataBuffer)
+        {
+            _ASSERTE(!"LOONGARCH64: not implementation on loongarch64!!!");
+            LIMITED_METHOD_CONTRACT;
+
+            _ASSERTE(refSize == InstructionFormat::k32);
+
+            if (fixedUpReference < -1048576 || fixedUpReference > 1048572)
+                COMPlusThrow(kNotSupportedException);
+
+            _ASSERTE((fixedUpReference & 0x3) == 0);
+            DWORD imm19 = (DWORD)(0x7FFFF & (fixedUpReference >> 2));
+
+            pOutBufferRW[0] = static_cast<BYTE>((0x7 & imm19 /* Bits2-0(imm19) */) << 5  | (0xF & variationCode /* cond */));
+            pOutBufferRW[1] = static_cast<BYTE>((0x7F8 & imm19 /* Bits10-3(imm19) */) >> 3);
+            pOutBufferRW[2] = static_cast<BYTE>((0x7F800 & imm19 /* Bits19-11(imm19) */) >> 11);
+            pOutBufferRW[3] = static_cast<BYTE>(0x54);
+        }
+};
+
+//-----------------------------------------------------------------------
+// InstructionFormat for JIRL (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- jirl $r0/1,$r21,0
+
+    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);
+
+                UINT32 imm16 = (UINT32)(0xFFF & dataOffset);
+                //pcaddi $r21,0
+                //ld.d  $r21,$r21,dataOffset
+                //ld.d  $r21,$r21,0
+                //jirl  $r0/1,$r21,0
+
+                *(DWORD*)pOutBufferRW = 0x18000015 | ((dataOffset >>14) <<5);//pcaddi $r21,0
+                *(DWORD*)(pOutBufferRW + 4) = 0x28c002b5 | (imm16 << 10);//ld.d  $r21,$r21,dataOffset-low12
+                *(DWORD*)(pOutBufferRW+8) = 0x28c002b5;//ld.d  $r21,$r21,0
+                if (IsCall(variationCode))
+                {
+                    *(DWORD*)(pOutBufferRW+12) = 0x4c0002a1;//jirl  $ra,$r21,0
+                }
+                else
+                {
+                    *(DWORD*)(pOutBufferRW+12) = 0x4c0002a0;//jirl  $r0,$r21,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);
+                //pcaddi $r21,0
+                //ld.d  $r21,$r21,dataOffset
+                //jirl  $r0/1,$r21,0
+
+                *(DWORD*)pOutBufferRW = 0x18000015 | ((dataOffset >>14) <<5);//pcaddi $r21,0
+                *(DWORD*)(pOutBufferRW + 4) = 0x28c002b5 | (imm12 << 10);//ld.d  $r21,$r21,dataOffset-low12
+                if (IsCall(variationCode))
+                {
+                    *((DWORD*)(pOutBufferRW+8)) = 0x4c0002a1;//jirl  $ra,$r21,0
+                }
+                else
+                {
+                    *((DWORD*)(pOutBufferRW+8)) = 0x4c0002a0;//jirl  $r0,$r21,0
+                }
+
+                if (!ClrSafeInt<__int64>::addition(fixedUpReference, (__int64)pOutBufferRX, fixedUpReference))
+                    COMPlusThrowArithmetic();
+                *((__int64*)pDataBuffer) = fixedUpReference;
+            }
+        }
+
+};
+
+//-----------------------------------------------------------------------
+// InstructionFormat for loading a label to the register (pcaddi/ld.d)
+//-----------------------------------------------------------------------
+class LoadFromLabelInstructionFormat : public InstructionFormat
+{
+    public:
+        LoadFromLabelInstructionFormat() : InstructionFormat( InstructionFormat::k32)
+        {
+            LIMITED_METHOD_CONTRACT;
+        }
+
+        virtual UINT GetSizeOfInstruction(UINT refSize, UINT variationCode)
+        {
+            _ASSERTE(!"LOONGARCH64: not implementation on loongarch64!!!");
+            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)
+        {
+            return fExternal;
+        }
+
+        virtual VOID EmitInstruction(UINT refSize, __int64 fixedUpReference, BYTE *pOutBufferRX, BYTE *pOutBufferRW, UINT variationCode, BYTE *pDataBuffer)
+        {
+            _ASSERTE(!"LOONGARCH64: not implementation on loongarch64!!!");
+            LIMITED_METHOD_CONTRACT;
+            // VariationCode is used to indicate the register the label is going to be loaded
+
+            DWORD imm =(DWORD)(fixedUpReference>>12);
+            if (imm>>21)
+                COMPlusThrow(kNotSupportedException);
+
+            // pcaddi r21, #Page_of_fixedUpReference
+            *((DWORD*)pOutBufferRW) = 0;
+
+            // ld.d  r21, [r21, #offset_of_fixedUpReference_to_its_page]
+            UINT64 target = (UINT64)(fixedUpReference + pOutBufferRX)>>3;
+            *((DWORD*)(pOutBufferRW+4)) = 0;
+        }
+};
+
+
+
+static BYTE gConditionalBranchIF[sizeof(ConditionalBranchInstructionFormat)];
+static BYTE gBranchIF[sizeof(BranchInstructionFormat)];
+//static BYTE gLoadFromLabelIF[sizeof(LoadFromLabelInstructionFormat)];
+
+#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;
+    pRD->volatileCurrContextPointers.T7 = NULL;
+    pRD->volatileCurrContextPointers.T8 = NULL;
+    pRD->volatileCurrContextPointers.X0 = 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.S0 = 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.Fp = unwoundstate->captureCalleeSavedRegisters[9] = baseState->captureCalleeSavedRegisters[9];
+    context.Tp = unwoundstate->captureCalleeSavedRegisters[10] = baseState->captureCalleeSavedRegisters[10];
+    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.S0 = &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.Fp = &unwoundstate->captureCalleeSavedRegisters[9];
+    nonVolContextPtrs.Tp = &unwoundstate->captureCalleeSavedRegisters[10];
+    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.S0;
+    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.Fp;
+    unwoundstate->captureCalleeSavedRegisters[10] = context.Tp;
+#endif
+
+#ifdef DACCESS_COMPILE
+    // For DAC builds, we update the registers directly since we dont have context pointers
+    unwoundstate->captureCalleeSavedRegisters[0] = context.S0;
+    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.Fp;
+    unwoundstate->captureCalleeSavedRegisters[10] = context.Tp;
+#else // !DACCESS_COMPILE
+    // For non-DAC builds, update the register state from context pointers
+    unwoundstate->ptrCalleeSavedRegisters[0] = nonVolContextPtrs.S0;
+    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.Fp;
+    unwoundstate->ptrCalleeSavedRegisters[10] = 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->S0 = (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->Fp = (DWORD64)(pUnwoundState->captureCalleeSavedRegisters[9]);
+        pRD->pCurrentContext->Tp = (DWORD64)(pUnwoundState->captureCalleeSavedRegisters[10]);
+        pRD->pCurrentContext->Ra = NULL; // Unwind again to get Caller's PC
+
+        pRD->pCurrentContextPointers->S0 = 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->Fp = pUnwoundState->ptrCalleeSavedRegisters[9];
+        pRD->pCurrentContextPointers->Tp = pUnwoundState->ptrCalleeSavedRegisters[10];
+        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->S0 = 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->Fp = m_MachState.ptrCalleeSavedRegisters[9] ? *m_MachState.ptrCalleeSavedRegisters[9] : m_MachState.captureCalleeSavedRegisters[9];
+    pRD->pCurrentContext->Tp = m_MachState.ptrCalleeSavedRegisters[10] ? *m_MachState.ptrCalleeSavedRegisters[10] : m_MachState.captureCalleeSavedRegisters[10];
+    pRD->pCurrentContext->Ra = NULL; // Unwind again to get Caller's PC
+#else // TARGET_UNIX
+    pRD->pCurrentContext->S0 = *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->Fp = *m_MachState.ptrCalleeSavedRegisters[9];
+    pRD->pCurrentContext->Tp = *m_MachState.ptrCalleeSavedRegisters[10];
+    pRD->pCurrentContext->Ra = NULL; // Unwind again to get Caller's PC
+#endif
+
+#if !defined(DACCESS_COMPILE)
+    pRD->pCurrentContextPointers->S0 = 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->Fp = m_MachState.ptrCalleeSavedRegisters[9];
+    pRD->pCurrentContextPointers->Tp = m_MachState.ptrCalleeSavedRegisters[10];
+    pRD->pCurrentContextPointers->Ra = NULL; // Unwind again to get Caller's PC
+#endif
+    ClearRegDisplayArgumentAndScratchRegisters(pRD);
+}
+
+#ifndef DACCESS_COMPILE
+
+void ThisPtrRetBufPrecode::Init(MethodDesc* pMD, LoaderAllocator *pLoaderAllocator)
+{
+    WRAPPER_NO_CONTRACT;
+
+    //Initially
+    //a0 -This ptr
+    //a1 -ReturnBuffer
+    m_rgCode[0] = 0x18000055; //pcaddi  r21,2
+    m_rgCode[1] = 0x28c042b5; //ld.d  r21,16(r21)
+    m_rgCode[2] = 0x0380008f; //ori  r15,a0,0x0
+    m_rgCode[3] = 0x038000a4; //ori  a0,a1,0x0
+    m_rgCode[4] = 0x038001e5; //ori  a1,r15,0x0
+    m_rgCode[5] = 0x4c0002a0; //jirl  r0,r21,0
+
+    _ASSERTE((UINT32*)&m_pTarget == &m_rgCode[6]);
+    _ASSERTE(6 == ARRAY_SIZE(m_rgCode));
+
+    m_pTarget = GetPreStubEntryPoint();
+    m_pMethodDesc = (TADDR)pMD;
+}
+
+#endif // !DACCESS_COMPILE
+
+void UpdateRegDisplayFromCalleeSavedRegisters(REGDISPLAY * pRD, CalleeSavedRegisters * pCalleeSaved)
+{
+    LIMITED_METHOD_CONTRACT;
+    pRD->pCurrentContext->S0 = pCalleeSaved->s0;
+    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->Tp = pCalleeSaved->tp;
+    pRD->pCurrentContext->Fp  = pCalleeSaved->fp;
+    pRD->pCurrentContext->Ra  = pCalleeSaved->ra;
+
+    T_KNONVOLATILE_CONTEXT_POINTERS * pContextPointers = pRD->pCurrentContextPointers;
+    pContextPointers->S0 = (PDWORD64)&pCalleeSaved->s0;
+    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->Tp = (PDWORD64)&pCalleeSaved->tp;
+    pContextPointers->Fp = (PDWORD64)&pCalleeSaved->fp;
+    pContextPointers->Ra  = (PDWORD64)&pCalleeSaved->ra;
+}
+
+void TransitionFrame::UpdateRegDisplay(const PREGDISPLAY pRD)
+{
+    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)
+{
+    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->S0 = (PDWORD64)&m_ctx.S0;
+    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->Fp = (PDWORD64)&m_ctx.Fp;
+    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)
+{
+    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->S0 = NULL;
+    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->Tp = NULL;
+
+    pRD->ControlPC = m_pCallerReturnAddress;
+    pRD->SP = (DWORD64) dac_cast<TADDR>(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;
+}
+
+#ifdef FEATURE_HIJACK
+TADDR ResumableFrame::GetReturnAddressPtr(void)
+{
+    LIMITED_METHOD_DAC_CONTRACT;
+    return dac_cast<TADDR>(m_Regs) + offsetof(T_CONTEXT, Pc);
+}
+
+void ResumableFrame::UpdateRegDisplay(const PREGDISPLAY pRD)
+{
+    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->S0 = &m_Regs->S0;
+    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->Tp = &m_Regs->Tp;
+    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->volatileCurrContextPointers.T7 = &m_Regs->T7;
+    pRD->volatileCurrContextPointers.T8 = &m_Regs->T8;
+    pRD->volatileCurrContextPointers.X0 = &m_Regs->X0;
+
+    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)
+{
+    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->S0 = m_Args->S0;
+    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->Tp = m_Args->Tp;
+    pRD->pCurrentContext->Fp = m_Args->Fp;
+    pRD->pCurrentContext->Ra = m_Args->Ra;
+
+    pRD->pCurrentContextPointers->S0 = &m_Args->S0;
+    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->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
+
+#ifdef FEATURE_COMINTEROP
+
+void emitCOMStubCall (ComCallMethodDesc *pCOMMethodRX, ComCallMethodDesc *pCOMMethodRW, PCODE target)
+
+{
+    WRAPPER_NO_CONTRACT;
+
+    // pcaddi  $r21,0
+       // ld.d  $t2, label_comCallMethodDesc
+       // ld.d  $r21, label_target
+       // jirl  $r0,$r21,0
+       // label_target:
+    // target address (8 bytes)
+    // label_comCallMethodDesc:
+    DWORD rgCode[] = {
+        0x0,
+        0x0,
+        0x0
+    };
+
+    _ASSERTE(!"LOONGARCH64: not implementation on loongarch64!!!");
+
+    BYTE *pBufferRX = (BYTE*)pCOMMethodRX - COMMETHOD_CALL_PRESTUB_SIZE;
+    BYTE *pBufferRW = (BYTE*)pCOMMethodRW - COMMETHOD_CALL_PRESTUB_SIZE;
+
+    memcpy(pBufferRW, rgCode, sizeof(rgCode));
+    *((PCODE*)(pBufferRW + sizeof(rgCode) + 4)) = target;
+
+    // Ensure that the updated instructions get actually written
+    ClrFlushInstructionCache(pBufferRX, COMMETHOD_CALL_PRESTUB_SIZE);
+
+    _ASSERTE(IS_ALIGNED(pBufferRX + COMMETHOD_CALL_PRESTUB_ADDRESS_OFFSET, sizeof(void*)) &&
+             *((PCODE*)(pBufferRX + COMMETHOD_CALL_PRESTUB_ADDRESS_OFFSET)) == target);
+}
+#endif // FEATURE_COMINTEROP
+
+
+void JIT_TailCall()
+{
+    _ASSERTE(!"LOONGARCH64: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;
+    ExecutableWriterHolder<BYTE> writeBarrierWriterHolder;
+    if (IsWriteBarrierCopyEnabled())
+    {
+        writeBarrierWriterHolder = ExecutableWriterHolder<BYTE>(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<PTR_PTR_CONTEXT>((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<PTR_PTR_CONTEXT>((TADDR)stackSlot);
+    return *ppContext;
+}
+
+void RedirectForThreadAbort()
+{
+    // ThreadAbort is not supported in .net core
+    throw "NYI";
+}
+
+#if !defined(DACCESS_COMPILE)
+FaultingExceptionFrame *GetFrameFromRedirectedStubStackFrame (DISPATCHER_CONTEXT *pDispatcherContext)
+{
+    LIMITED_METHOD_CONTRACT;
+
+    return (FaultingExceptionFrame*)((TADDR)pDispatcherContext->ContextRecord->S0);
+}
+
+
+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) != 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] == 0x18000095) && //pcaddi  $r21,4
+        (pCode->m_code[1] == 0x28c022ae) && //ld.d  $t2,$r21,8
+        (pCode->m_code[2] == 0x28c002b5) && //ld.d  $r21,$r21,0
+        (pCode->m_code[3] == 0x4c0002a0))   //jirl  $r0,$r21,0
+    {
+        return (UMEntryThunk*)pCode->m_pvSecretParam;
+    }
+
+    return NULL;
+}
+
+void UMEntryThunkCode::Encode(UMEntryThunkCode *pEntryThunkCodeRX, BYTE* pTargetCode, void* pvSecretParam)
+{
+    // pcaddi  $r21,4
+    // ld.d  $t2,$r21,8
+    // ld.d  $r21,$r21,0
+    // jirl  $r0,$r21,0
+    // m_pTargetCode data
+    // m_pvSecretParam data
+
+    m_code[0] = 0x18000095; //pcaddi  $r21,4
+    m_code[1] = 0x28c022ae; //ld.d  $t2,$r21,8
+    m_code[2] = 0x28c002b5; //ld.d  $r21,$r21,0
+    m_code[3] = 0x4c0002a0; //jirl  $r0,$r21,0
+
+    m_pTargetCode = (TADDR)pTargetCode;
+    m_pvSecretParam = (TADDR)pvSecretParam;
+    FlushInstructionCache(GetCurrentProcess(),&pEntryThunkCodeRX->m_code,sizeof(m_code));
+}
+
+#ifndef DACCESS_COMPILE
+
+void UMEntryThunkCode::Poison()
+{
+    ExecutableWriterHolder<UMEntryThunkCode> thunkWriterHolder(this, sizeof(UMEntryThunkCode));
+    UMEntryThunkCode *pThisRW = thunkWriterHolder.GetRW();
+
+    pThisRW->m_pTargetCode = (TADDR)UMEntryThunk::ReportViolation;
+
+    // ld.d $a0,$r21,8
+    pThisRW->m_code[1] = 0x28c022a4;
+
+    ClrFlushInstructionCache(&m_code,sizeof(m_code));
+}
+
+#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 loongarch64, 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(!"LOONGARCH64:NYI");
+    return FALSE;
+}
+#endif // DACCESS_COMPILE
+
+#ifndef DACCESS_COMPILE
+// ----------------------------------------------------------------
+// StubLinkerCPU methods
+// ----------------------------------------------------------------
+
+void StubLinkerCPU::EmitMovConstant(IntReg target, UINT64 constant)
+{
+    // Move the 64bit constant into targetReg.
+
+    _ASSERTE((0 <= target) && ( target <= 31));
+
+    Emit32((DWORD)(0x02c00000 | target)); // addi.d  target, $r0, 0
+
+    //TODO: maybe optimize further.
+    if (0 == (constant >> 12))
+    {
+        Emit32((DWORD)(0x03800000 | (constant & 0xFFF)<<10 | target<<5 | target)); // ori  target, target, constant
+    }
+    else if (0 == (constant >> 32))
+    {
+        Emit32((DWORD)(0x14000000 | ((constant>>12) & 0xFFFFF)<<5 | target));      // lu12i.w  target, constant>>12
+        Emit32((DWORD)(0x03800000 | (constant & 0xFFF)<<10 | target<<5 | target)); // ori  target, target, constant
+
+        if (constant & 0x80000000)
+        {
+            Emit32((DWORD)(0x00DF0000 | target<<5 | target)); // bstrpick.d  target, target ,31 ,0
+        }
+    }
+    else if (0 == (constant >> 52))
+    {
+        Emit32((DWORD)(0x14000000 | ((constant>>12) & 0xFFFFF)<<5 | target));      // lu12i.w  target, constant>>12
+        Emit32((DWORD)(0x03800000 | (constant & 0xFFF)<<10 | target<<5 | target)); // ori  target, target, constant
+        Emit32((DWORD)(0x16000000 | ((constant>>32) & 0xFFFFF)<<5 | target));      // lu32i.d  target, constant>>32
+
+        if ((constant>>32) & 0x80000)
+        {
+            Emit32((DWORD)(0x00F30000 | target<<5 | target));  // bstrpick.d  target, target ,51 ,0
+        }
+    }
+    else
+    {
+        Emit32((DWORD)(0x14000000 | ((constant>>12) & 0xFFFFF)<<5 | target));      // lu12i.w  target, constant>>12
+        Emit32((DWORD)(0x03800000 | (constant & 0xFFF)<<10 | target<<5 | target)); // ori  target, target, constant
+        Emit32((DWORD)(0x16000000 | ((constant>>32) & 0xFFFFF)<<5 | target));      // lu32i.d  target, constant>>32
+        Emit32((DWORD)(0x03000000 | ((constant>>52)<<10) | target<<5 | target));   // lu52i.d  target, target, constant>>52
+    }
+}
+
+void StubLinkerCPU::EmitJumpRegister(IntReg regTarget)
+{
+    // jirl $r0,$regTarget,0
+    Emit32(0x4c000000 | (regTarget.reg << 5));
+}
+
+void StubLinkerCPU::EmitLoadStoreRegPairImm(DWORD flags, IntReg Rt1, IntReg Rt2, IntReg Rn, int offset)
+{
+    EmitLoadStoreRegPairImm(flags, (int)Rt1, (int)Rt2, Rn, offset, FALSE);
+}
+
+void StubLinkerCPU::EmitLoadStoreRegPairImm(DWORD flags, VecReg Vt1, VecReg Vt2, IntReg Rn, int offset)
+{
+    EmitLoadStoreRegPairImm(flags, (int)Vt1, (int)Vt2, Rn, offset, TRUE);
+}
+
+void StubLinkerCPU::EmitLoadStoreRegPairImm(DWORD flags, int regNum1, int regNum2, IntReg Rn, int offset, BOOL isVec)
+{
+    _ASSERTE(isVec == FALSE); // TODO: VecReg not supported yet
+    _ASSERTE((-2048 <= offset) && (offset < 2047));
+    _ASSERTE((offset & 7) == 0);
+
+    BOOL isLoad = flags & 1;
+    if (isLoad) {
+        // ld.d(regNum1, Rn, offset);
+        Emit32(emitIns_O_R_R_I(0xa3, regNum1, Rn, offset));
+        // ld.d(regNum2, Rn, offset + 8);
+        Emit32(emitIns_O_R_R_I(0xa3, regNum2, Rn, offset + 8));
+    } else {
+        // st.d(regNum1, Rn, offset);
+        Emit32(emitIns_O_R_R_I(0xa7, regNum1, Rn, offset));
+        // st.d(regNum2, Rn, offset + 8);
+        Emit32(emitIns_O_R_R_I(0xa7, regNum2, Rn, offset + 8));
+    }
+}
+
+void StubLinkerCPU::EmitLoadStoreRegImm(DWORD flags, IntReg Rt, IntReg Rn, int offset, int log2Size)
+{
+    EmitLoadStoreRegImm(flags, (int)Rt, Rn, offset, FALSE, log2Size);
+}
+
+void StubLinkerCPU::EmitFloatLoadStoreRegImm(DWORD flags, FloatReg Ft, IntReg Rn, int offset)
+{
+    BOOL isLoad    = flags & 1;
+    if (isLoad) {
+        // fld.d(Ft, Rn, offset);
+        Emit32(emitIns_O_R_R_I(0xae, (int)Ft & 0x1f, Rn, offset));
+    } else {
+        // fst.d(Ft, Rn, offset);
+        Emit32(emitIns_O_R_R_I(0xaf, (int)Ft & 0x1f, Rn, offset));
+    }
+}
+
+void StubLinkerCPU::EmitLoadStoreRegImm(DWORD flags, int regNum, IntReg Rn, int offset, BOOL isVec, int log2Size)
+{
+    _ASSERTE((log2Size & ~0x3ULL) == 0);
+
+    _ASSERTE(isVec == FALSE); // TODO: VecReg not supported yet
+    BOOL isLoad    = flags & 1;
+    if (isLoad) {
+        // ld.d(regNum, Rn, offset);
+        Emit32(emitIns_O_R_R_I(0xa3, regNum, Rn, offset));
+    } else {
+        // st.d(regNum, Rn, offset);
+        Emit32(emitIns_O_R_R_I(0xa7, regNum, Rn, offset));
+    }
+}
+
+void StubLinkerCPU::EmitLoadFloatRegImm(FloatReg ft, IntReg base, int offset)
+{
+    // fld.d ft,base,offset
+    _ASSERTE(offset <= 2047 && offset >= -2048);
+    Emit32(0x2b800000 | (base.reg << 5) | ((offset & 0xfff)<<10) | ft.reg);
+}
+
+void StubLinkerCPU::EmitMovReg(IntReg Rd, IntReg Rm)
+{
+    // ori(Rd, Rm, 0);
+    Emit32(0x03800000 | (Rm.reg << 5) | Rd.reg);
+}
+
+void StubLinkerCPU::EmitMovFloatReg(FloatReg Fd, FloatReg Fs)
+{
+    // fmov.d fd, fs
+    Emit32(0x01149800 | Fd.reg | (Fs.reg << 5));
+}
+
+void StubLinkerCPU::EmitSubImm(IntReg Rd, IntReg Rn, unsigned int value)
+{
+    _ASSERTE(value <= 2047);
+    int tmp_value = -(int)value;
+    // addi.d(Rd, Rn, -value);
+    Emit32(0x02c00000 | (Rn.reg << 5) | Rd.reg | ((tmp_value & 0xfff)<<10));
+}
+
+void StubLinkerCPU::EmitAddImm(IntReg Rd, IntReg Rn, unsigned int value)
+{
+    _ASSERTE(value <= 2047);
+    // addi.d(Rd, Rn, value);
+    Emit32(0x02c00000 | (Rn.reg << 5) | Rd.reg | ((value & 0xfff)<<10));
+}
+
+void StubLinkerCPU::Init()
+{
+    new (gConditionalBranchIF) ConditionalBranchInstructionFormat();
+    new (gBranchIF) BranchInstructionFormat();
+    //new (gLoadFromLabelIF) LoadFromLabelInstructionFormat();
+}
+
+// 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 $r21. Tailcall to the target method after re-arranging the arguments
+    // ld.d  $r21, $r4, offsetof(DelegateObject, _methodPtrAux)
+    EmitLoadStoreRegImm(eLOAD, IntReg(21)/*$r21*/, IntReg(4)/*$a0*/, DelegateObject::GetOffsetOfMethodPtrAux());
+    // addi.d  t8, a0, DelegateObject::GetOffsetOfMethodPtrAux() - load the indirection cell into t8 used by ResolveWorkerAsmStub
+    EmitAddImm(20/*$t8*/, 4, 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)
+            {
+                // FirstFloatReg is 0;
+                int j = 1;
+                while (pEntry[j].srcofs & ShuffleEntry::FPREGMASK)
+                {
+                    j++;
+                }
+                assert((pEntry->dstofs - pEntry->srcofs) == index);
+                assert(8 > index);
+
+                int tmp_reg = 11;//f11.
+                ShuffleEntry* tmp_entry = pShuffleEntryArray + delay_index[0];
+                while (index)
+                {
+                    // fld.d/s(Ft, sp, offset);
+                    Emit32(emitIns_O_R_R_I(0xae, tmp_reg++, 3/*sp*/, tmp_entry->srcofs << 3));
+
+                    index--;
+                    tmp_entry++;
+                }
+
+                j -= 1;
+                tmp_entry = pEntry + j;
+                i += j;
+                while (pEntry[j].srcofs & ShuffleEntry::FPREGMASK)
+                {
+                    if (pEntry[j].dstofs & ShuffleEntry::FPREGMASK)// fmov.d fd, fs
+                        Emit32(0x01149800 | (pEntry[j].dstofs & ShuffleEntry::OFSREGMASK) | ((pEntry[j].srcofs & ShuffleEntry::OFSREGMASK) << 5));
+                    else //// fst.d(Ft, Rn, offset);
+                        Emit32(emitIns_O_R_R_I(0xaf, (pEntry[j].srcofs & ShuffleEntry::OFSREGMASK), 3, pEntry[j].dstofs * sizeof(long)));
+                    j--;
+                }
+                while (tmp_reg > 11)
+                {
+                    tmp_reg--;
+                    // fmov.d fd, fs
+                    Emit32(0x01149800 | index | (tmp_reg << 5));
+                    index++;
+                }
+                index = 0;
+                pEntry = tmp_entry;
+            }
+            else
+            {
+                // 4 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) + 4), IntReg((pEntry->srcofs & ShuffleEntry::OFSMASK) + 4));
+            }
+        }
+        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)), RegSp, pEntry->srcofs * sizeof(void*));
+            }
+            else
+            {
+                assert(pEntry->dstofs & ShuffleEntry::REGMASK);
+                EmitLoadStoreRegImm(eLOAD, IntReg((pEntry->dstofs & ShuffleEntry::OFSMASK) + 4), 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(16)/*t4*/, RegSp, pEntry->srcofs * sizeof(void*));
+            EmitLoadStoreRegImm(eSTORE, IntReg(16)/*t4*/, RegSp, pEntry->dstofs * sizeof(void*));
+        }
+    }
+
+    // Tailcall to target
+    // jirl  $r0,$r21,0
+    EmitJumpRegister(21);
+}
+
+// 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 += 4;//NOTE: LOONGARCH64 should start at a0=4;
+
+        if (extraArg == NULL)
+        {
+            if (pSharedMD->RequiresInstMethodTableArg())
+            {
+                // Unboxing stub case
+                // Fill param arg with methodtable of this pointer
+                // ld.d regHidden, a0, 0
+                EmitLoadStoreRegImm(eLOAD, IntReg(regHidden), IntReg(4), 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.d a0, a0, sizeof(MethodDesc*)
+        EmitAddImm(IntReg(4), IntReg(4), 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<BranchInstructionFormat::VariationCodes>(variationCode | BranchInstructionFormat::VariationCodes::BIF_VAR_CALL);
+    if (fIndirect)
+        variationCode = static_cast<BranchInstructionFormat::VariationCodes>(variationCode | BranchInstructionFormat::VariationCodes::BIF_VAR_INDIRECT);
+
+    EmitLabelRef(target, reinterpret_cast<BranchInstructionFormat&>(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) \
+    SIZE_T cb = size; \
+    SIZE_T cbAligned = ALIGN_UP(cb, DYNAMIC_HELPER_ALIGNMENT); \
+    BYTE * pStartRX = (BYTE *)(void*)pAllocator->GetDynamicHelpersHeap()->AllocAlignedMem(cbAligned, DYNAMIC_HELPER_ALIGNMENT); \
+    ExecutableWriterHolder<BYTE> startWriterHolder(pStartRX, cbAligned); \
+    BYTE * pStart = startWriterHolder.GetRW(); \
+    size_t rxOffset = pStartRX - pStart; \
+    BYTE * p = pStart;
+
+#define END_DYNAMIC_HELPER_EMIT() \
+    _ASSERTE(pStart + cb == p); \
+    while (p < pStart + cbAligned) { *(DWORD*)p = 0xffffff0f/*badcode*/; p += 4; }\
+    ClrFlushInstructionCache(pStart, cbAligned); \
+    return (PCODE)pStart
+
+PCODE DynamicHelpers::CreateHelper(LoaderAllocator * pAllocator, TADDR arg, PCODE target)
+{
+    STANDARD_VM_CONTRACT;
+
+    BEGIN_DYNAMIC_HELPER_EMIT(32);
+
+    *(DWORD*)p = 0x18000015;// pcaddi  $r21,0
+    p += 4;
+    *(DWORD*)p = 0x28c042a4;// ld.d  $a0,$r21,16
+    p += 4;
+    *(DWORD*)p = 0x28c062b5;// ld.d  $r21,$r21,24
+    p += 4;
+    *(DWORD*)p = 0x4c0002a0;// jirl  $r0,$r21,0
+    p += 4;
+
+    // label:
+    // arg
+    *(TADDR*)p = arg;
+    p += 8;
+    // target
+    *(PCODE*)p = target;
+    p += 8;
+
+    END_DYNAMIC_HELPER_EMIT();
+}
+
+// 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)
+{
+    STANDARD_VM_CONTRACT;
+
+    *(DWORD*)p = 0x18000015;// pcaddi  $r21,0
+    p += 4;
+    *(DWORD*)p = 0x28c042a5;// ld.d  $a1,$r21,16
+    p += 4;
+    *(DWORD*)p = 0x28c062b5;// ld.d  $r21,$r21,24
+    p += 4;
+    *(DWORD*)p = 0x4c0002a0;// jirl  $r0,$r21,0
+    p += 4;
+
+    _ASSERTE(!((uintptr_t)p & 0x7));
+
+    // label:
+    // arg
+    *(TADDR*)p = arg;
+    p += 8;
+    // target
+    *(PCODE*)p = target;
+    p += 8;
+}
+
+PCODE DynamicHelpers::CreateHelperWithArg(LoaderAllocator * pAllocator, TADDR arg, PCODE target)
+{
+    STANDARD_VM_CONTRACT;
+
+    BEGIN_DYNAMIC_HELPER_EMIT(32);
+
+    EmitHelperWithArg(p, rxOffset, pAllocator, arg, target);
+
+    END_DYNAMIC_HELPER_EMIT();
+}
+
+PCODE DynamicHelpers::CreateHelper(LoaderAllocator * pAllocator, TADDR arg, TADDR arg2, PCODE target)
+{
+    STANDARD_VM_CONTRACT;
+
+    BEGIN_DYNAMIC_HELPER_EMIT(48);
+
+    *(DWORD*)p = 0x18000015;// pcaddi  $r21,0
+    p += 4;
+    *(DWORD*)p = 0x28c062a4;// ld.d  $a0,$r21,24
+    p += 4;
+    *(DWORD*)p = 0x28c082a5;// ld.d  $a1,$r21,32
+    p += 4;
+    *(DWORD*)p = 0x28c0a2b5;// ld.d  $r21,$r21,40
+    p += 4;
+    *(DWORD*)p = 0x4c0002a0;// jirl  $r0,$r21,0
+    p += 4;
+
+    // nop, padding to make 8 byte aligned
+    *(DWORD*)p = 0x03400000;
+    p += 4;
+
+    // label:
+    // arg
+    *(TADDR*)p = arg;
+    p += 8;
+    // arg2
+    *(TADDR*)p = arg2;
+    p += 8;
+    // target
+    *(TADDR*)p = target;
+    p += 8;
+
+    END_DYNAMIC_HELPER_EMIT();
+}
+
+PCODE DynamicHelpers::CreateHelperArgMove(LoaderAllocator * pAllocator, TADDR arg, PCODE target)
+{
+    STANDARD_VM_CONTRACT;
+
+    BEGIN_DYNAMIC_HELPER_EMIT(40);
+
+    *(DWORD*)p = 0x18000015;// pcaddi  $r21,0
+    p += 4;
+    *(DWORD*)p = 0x03800085;// ori  $a1,$a0,0
+    p += 4;
+    *(DWORD*)p = 0x28c062a4;// ld.d  $a0,$r21,24
+    p += 4;
+    *(DWORD*)p = 0x28c082b5;// ld.d  $r21,$r21,32
+    p += 4;
+    *(DWORD*)p = 0x4c0002a0;// jirl  $r0,$r21,0
+    p += 4;
+
+    // nop, padding to make 8 byte aligned
+    *(DWORD*)p = 0x03400000;
+    p += 4;
+
+    // label:
+    // arg
+    *(TADDR*)p = arg;
+    p += 8;
+    // target
+    *(TADDR*)p = target;
+    p += 8;
+
+    END_DYNAMIC_HELPER_EMIT();
+}
+
+PCODE DynamicHelpers::CreateReturn(LoaderAllocator * pAllocator)
+{
+    STANDARD_VM_CONTRACT;
+
+    BEGIN_DYNAMIC_HELPER_EMIT(4);
+
+    *(DWORD*)p = 0x4c000020;// jirl  $r0,$ra,0
+    p += 4;
+
+    END_DYNAMIC_HELPER_EMIT();
+}
+
+PCODE DynamicHelpers::CreateReturnConst(LoaderAllocator * pAllocator, TADDR arg)
+{
+    STANDARD_VM_CONTRACT;
+
+    BEGIN_DYNAMIC_HELPER_EMIT(24);
+
+    *(DWORD*)p = 0x18000015;// pcaddi  $r21,0
+    p += 4;
+    *(DWORD*)p = 0x28c042a4;// ld.d  $v0,$r21,16
+    p += 4;
+    *(DWORD*)p = 0x4c000020;// jirl  $r0,$ra,0
+    p += 4;
+    *(DWORD*)p = 0x03400000;// nop, padding to make 8 byte aligned
+    p += 4;
+
+    // label:
+    // arg
+    *(TADDR*)p = arg;
+    p += 8;
+
+    END_DYNAMIC_HELPER_EMIT();
+}
+
+PCODE DynamicHelpers::CreateReturnIndirConst(LoaderAllocator * pAllocator, TADDR arg, INT8 offset)
+{
+    STANDARD_VM_CONTRACT;
+
+    BEGIN_DYNAMIC_HELPER_EMIT(32);
+
+    *(DWORD*)p = 0x18000015;// pcaddi  $r21,0
+    p += 4;
+    *(DWORD*)p = 0x28c062a4;// ld.d  $v0,$r21,24
+    p += 4;
+    *(DWORD*)p = 0x28c00084;// ld.d  $v0,$v0,0
+    p += 4;
+    *(DWORD*)p = 0x02c00084 | ((offset & 0xfff)<<10);// addi.d  $v0,$v0,offset
+    p += 4;
+    *(DWORD*)p = 0x4c000020;// jirl  $r0,$ra,0
+    p += 4;
+    *(DWORD*)p = 0x03400000;// nop, padding to make 8 byte aligned
+    p += 4;
+
+    // label:
+    // arg
+    *(TADDR*)p = arg;
+    p += 8;
+
+    END_DYNAMIC_HELPER_EMIT();
+}
+
+PCODE DynamicHelpers::CreateHelperWithTwoArgs(LoaderAllocator * pAllocator, TADDR arg, PCODE target)
+{
+    STANDARD_VM_CONTRACT;
+
+    BEGIN_DYNAMIC_HELPER_EMIT(32);
+
+    *(DWORD*)p = 0x18000015;// pcaddi  $r21,0
+    p += 4;
+    *(DWORD*)p = 0x28c042a6;// ld.d  $a2,$r21,16
+    p += 4;
+    *(DWORD*)p = 0x28c062b5;// ld.d  $r21,$r21,24
+    p += 4;
+    *(DWORD*)p = 0x4c0002a0;// jirl  $r0,$r21,0
+    p += 4;
+
+    // label:
+    // arg
+    *(TADDR*)p = arg;
+    p += 8;
+
+    // target
+    *(TADDR*)p = target;
+    p += 8;
+    END_DYNAMIC_HELPER_EMIT();
+}
+
+PCODE DynamicHelpers::CreateHelperWithTwoArgs(LoaderAllocator * pAllocator, TADDR arg, TADDR arg2, PCODE target)
+{
+    STANDARD_VM_CONTRACT;
+
+    BEGIN_DYNAMIC_HELPER_EMIT(48);
+
+    *(DWORD*)p = 0x18000015;// pcaddi  $r21,0
+    p += 4;
+    *(DWORD*)p = 0x28c062a6;// ld.d  $a2,$r21,24
+    p += 4;
+    *(DWORD*)p = 0x28c082a7;// ld.d  $a3,$r21,32
+    p += 4;
+    *(DWORD*)p = 0x28c0a2b5;// ld.d  $r21,$r21,40
+    p += 4;
+    *(DWORD*)p = 0x4c0002a0;// jirl  $r0,$r21,0
+    p += 4;
+    *(DWORD*)p = 0xffffff0f;// badcode, padding to make 8 byte aligned
+    p += 4;
+
+    // label:
+    // arg
+    *(TADDR*)p = arg;
+    p += 8;
+    // arg2
+    *(TADDR*)p = arg2;
+    p += 8;
+    // target
+    *(TADDR*)p = target;
+    p += 8;
+    END_DYNAMIC_HELPER_EMIT();
+}
+
+PCODE DynamicHelpers::CreateDictionaryLookupHelper(LoaderAllocator * pAllocator, CORINFO_RUNTIME_LOOKUP * pLookup, DWORD dictionaryIndexAndSlot, Module * pModule)
+{
+    STANDARD_VM_CONTRACT;
+
+    PCODE helperAddress = (pLookup->helper == CORINFO_HELP_RUNTIMEHANDLE_METHOD ?
+        GetEEFuncEntryPoint(JIT_GenericHandleMethodWithSlotAndModule) :
+        GetEEFuncEntryPoint(JIT_GenericHandleClassWithSlotAndModule));
+
+    GenericHandleArgs * pArgs = (GenericHandleArgs *)(void *)pAllocator->GetDynamicHelpersHeap()->AllocAlignedMem(sizeof(GenericHandleArgs), DYNAMIC_HELPER_ALIGNMENT);
+    ExecutableWriterHolder<GenericHandleArgs> argsWriterHolder(pArgs, sizeof(GenericHandleArgs));
+    argsWriterHolder.GetRW()->dictionaryIndexAndSlot = dictionaryIndexAndSlot;
+    argsWriterHolder.GetRW()->signature = pLookup->signature;
+    argsWriterHolder.GetRW()->module = (CORINFO_MODULE_HANDLE)pModule;
+
+    WORD slotOffset = (WORD)(dictionaryIndexAndSlot & 0xFFFF) * sizeof(Dictionary*);
+
+    // It's available only via the run-time helper function
+    if (pLookup->indirections == CORINFO_USEHELPER)
+    {
+        BEGIN_DYNAMIC_HELPER_EMIT(32);
+
+        // a0 already contains generic context parameter
+        // reuse EmitHelperWithArg for below two operations
+        // a1 <- pArgs
+        // branch to helperAddress
+        EmitHelperWithArg(p, rxOffset, pAllocator, (TADDR)pArgs, helperAddress);
+
+        END_DYNAMIC_HELPER_EMIT();
+    }
+    else
+    {
+        int codeSize = 0;
+        int indirectionsDataSize = 0;
+        if (pLookup->sizeOffset != CORINFO_NO_SIZE_CHECK)
+        {
+            codeSize += (pLookup->sizeOffset > 2047 ? 8 : 4);
+            indirectionsDataSize += (pLookup->sizeOffset > 2047 ? 4 : 0);
+            codeSize += 12;
+        }
+
+        for (WORD i = 0; i < pLookup->indirections; i++) {
+            _ASSERTE(pLookup->offsets[i] >= 0);
+            codeSize += (pLookup->offsets[i] > 2047 ? 8 : 4); // if( > 2047) (8 bytes) else 4 bytes for instructions.
+            indirectionsDataSize += (pLookup->offsets[i] > 2047 ? 4 : 0); // 4 bytes for storing indirection offset values
+        }
+
+        codeSize += indirectionsDataSize ? 4 : 0; // pcaddi
+
+        if(pLookup->testForNull)
+        {
+            codeSize += 12; // ori-beq-jr
+
+            //padding for 8-byte align (required by EmitHelperWithArg)
+            if (codeSize & 0x7)
+                codeSize += 4;
+
+            codeSize += 32; // size of EmitHelperWithArg
+        }
+        else
+        {
+            codeSize += 4; /* jilr */
+        }
+
+        // the offset value of date_lable.
+        uint dataOffset = codeSize;
+
+        codeSize += indirectionsDataSize;
+
+        BEGIN_DYNAMIC_HELPER_EMIT(codeSize);
+
+        BYTE * old_p = p;
+
+        if (indirectionsDataSize)
+        {
+            // pcaddi  $r21,0
+            *(DWORD*)p = 0x18000015;
+            p += 4;
+        }
+
+        if (pLookup->testForNull || pLookup->sizeOffset != CORINFO_NO_SIZE_CHECK)
+        {
+            // ori  $t3,$a0,0
+            *(DWORD*)p = 0x0380008f;
+            p += 4;
+        }
+
+        BYTE* pBLECall = NULL;
+
+        for (WORD i = 0; i < pLookup->indirections; i++)
+        {
+            if (i == pLookup->indirections - 1 && pLookup->sizeOffset != CORINFO_NO_SIZE_CHECK)
+            {
+                _ASSERTE(pLookup->testForNull && i > 0);
+
+                if (pLookup->sizeOffset > 2047)
+                {
+                    // pcaddi  $r21,0
+                    *(DWORD*)p = 0x18000015; p += 4;
+                    // ld.d  $t4,$r21, #dataOffset
+                    *(DWORD*)p = 0x28c002b0 | (dataOffset << 10); p += 4;
+                    // ldx.d  $t5,$a0,$t4
+                    *(DWORD*)p = 0x380c4091; p += 4;
+
+                    // move to next indirection offset data
+                    dataOffset = dataOffset - 12 + 4; // subtract 12 as we have moved PC by 12 and add 4 as next data is at 4 bytes from previous data
+                }
+                else
+                {
+                    // ld.d $t5, $a0, #(pLookup->sizeOffset)
+                    *(DWORD*)p = 0x28c00091 | ((UINT32)pLookup->sizeOffset << 10); p += 4;
+                    dataOffset -= 4; // subtract 4 as we have moved PC by 4
+                }
+
+                // lu12i.w $t4, (slotOffset&0xfffff000)>>12
+                *(DWORD*)p = 0x14000010 | ((((UINT32)slotOffset & 0xfffff000) >> 12) << 5); p += 4;
+                // ori $t4, $t4, slotOffset&0xfff
+                *(DWORD*)p = 0x03800210 | (((UINT32)slotOffset & 0xfff) << 10); p += 4;
+                dataOffset -= 8;
+
+                // bge $t4,$t3, // CALL HELPER:
+                pBLECall = p;       // Offset filled later
+                *(DWORD*)p = 0x6400020f; p += 4;
+                dataOffset -= 4;
+            }
+
+            if(pLookup->offsets[i] > 2047)
+            {
+                _ASSERTE(dataOffset < 2047);
+                // ld.wu  $t4,$r21,0
+                *(DWORD*)p = 0x2a8002b0 | (dataOffset<<10);
+                p += 4;
+                dataOffset += 4;
+                // ldx.d  $a0,$a0,$t4
+                *(DWORD*)p = 0x380c4084;
+                p += 4;
+            }
+            else
+            {
+                // offset must be 8 byte aligned
+                _ASSERTE((pLookup->offsets[i] & 0x7) == 0);
+
+                // ld.d  $a0,$a0,pLookup->offsets[i]
+                *(DWORD*)p = 0x28c00084 | ((pLookup->offsets[i] & 0xfff)<<10);
+                p += 4;
+            }
+        }
+
+        // No null test required
+        if (!pLookup->testForNull)
+        {
+            _ASSERTE(pLookup->sizeOffset == CORINFO_NO_SIZE_CHECK);
+            // jirl  $r0,$ra,0
+            *(DWORD*)p = 0x4c000020;
+            p += 4;
+        }
+        else
+        {
+            // beq $a0,$zero, // CALL HELPER:
+            *(DWORD*)p = 0x58000880;
+            p += 4;
+
+            // jirl  $r0,$ra,0
+            *(DWORD*)p = 0x4c000020;
+            p += 4;
+
+            // CALL HELPER:
+            if(pBLECall != NULL)
+                *(DWORD*)pBLECall |= ((UINT32)(p - pBLECall) << 10);
+
+            // ori  $a0,$t3,0
+            *(DWORD*)p = 0x038001e4;
+            p += 4;
+            if ((uintptr_t)(p - old_p) & 0x7)
+            {
+                // nop, padding for 8-byte align (required by EmitHelperWithArg)
+                *(DWORD*)p = 0x03400000;
+                p += 4;
+            }
+
+            // reuse EmitHelperWithArg for below two operations
+            // a1 <- pArgs
+            // branch to helperAddress
+            EmitHelperWithArg(p, rxOffset, pAllocator, (TADDR)pArgs, helperAddress);
+        }
+
+        // datalabel:
+        for (WORD i = 0; i < pLookup->indirections; i++)
+        {
+            if (i == pLookup->indirections - 1 && pLookup->sizeOffset != CORINFO_NO_SIZE_CHECK && pLookup->sizeOffset > 2047)
+            {
+                *(UINT32*)p = (UINT32)pLookup->sizeOffset;
+                p += 4;
+            }
+            if(pLookup->offsets[i] > 2047)
+            {
+                *(UINT32*)p = (UINT32)pLookup->offsets[i];
+                p += 4;
+            }
+        }
+
+        END_DYNAMIC_HELPER_EMIT();
+    }
+}
+#endif // FEATURE_READYTORUN
+
+#endif // #ifndef DACCESS_COMPILE
diff --git a/src/coreclr/vm/loongarch64/thunktemplates.S b/src/coreclr/vm/loongarch64/thunktemplates.S
new file mode 100644 (file)
index 0000000..4ab9c1e
--- /dev/null
@@ -0,0 +1,38 @@
+// 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"
+
+LEAF_ENTRY StubPrecodeCode
+    pcaddi  $r21, 0x1004  //4, for Type encoding.
+    ld.d  $t2,$r21, (StubPrecodeData__MethodDesc - 4*4)
+    ld.d  $r21,$r21,  (StubPrecodeData__Target - 4*4)
+    jirl  $r0,$r21,0
+LEAF_END_MARKED StubPrecodeCode
+
+LEAF_ENTRY FixupPrecodeCode
+    pcaddi  $r21, 0x1003  //3, for Type encoding.
+    ld.d  $r21,$r21, (FixupPrecodeData__Target - 4*3)
+    jirl  $r0,$r21,0
+    pcaddi  $r21, 0x1003
+    ld.d  $t2,$r21,  (FixupPrecodeData__MethodDesc - 4*3 -4*3)
+    ld.d  $r21,$r21, (FixupPrecodeData__PrecodeFixupThunk - 4*3 - 4*3)
+    jirl  $r0,$r21,0
+LEAF_END_MARKED FixupPrecodeCode
+
+// NOTE: For LoongArch64 `CallCountingStubData__RemainingCallCountCell` must be zero !!!
+// Because the stub-identifying token is $t1 within the `OnCallCountThresholdReachedStub`.
+LEAF_ENTRY CallCountingStubCode
+    pcaddi  $t2, 0x1000
+    ld.d  $t1, $t2, CallCountingStubData__RemainingCallCountCell
+    ld.h  $r21, $t1, 0
+    addi.w  $r21, $r21, -1
+    st.h  $r21, $t1, 0
+    beq   $r21, $zero, LOCAL_LABEL(CountReachedZero)
+    ld.d  $r21, $t2, CallCountingStubData__TargetForMethod
+    jirl  $r0,$r21,0
+LOCAL_LABEL(CountReachedZero):
+    ld.d  $r21, $t2, CallCountingStubData__TargetForThresholdReached
+    jirl  $r0,$r21,0
+LEAF_END_MARKED CallCountingStubCode
diff --git a/src/coreclr/vm/loongarch64/unixstubs.cpp b/src/coreclr/vm/loongarch64/unixstubs.cpp
new file mode 100644 (file)
index 0000000..d51902a
--- /dev/null
@@ -0,0 +1,12 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+#include "common.h"
+
+extern "C"
+{
+    void RedirectForThrowControl()
+    {
+        PORTABILITY_ASSERT("Implement for PAL");
+    }
+};
diff --git a/src/coreclr/vm/loongarch64/virtualcallstubcpu.hpp b/src/coreclr/vm/loongarch64/virtualcallstubcpu.hpp
new file mode 100644 (file)
index 0000000..66630a6
--- /dev/null
@@ -0,0 +1,581 @@
+// 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_LOONGARCH64_H
+#define _VIRTUAL_CALL_STUB_LOONGARCH64_H
+
+#define DISPATCH_STUB_FIRST_DWORD 0x18000110
+#define RESOLVE_STUB_FIRST_DWORD  0x28c0008f
+#define VTABLECALL_STUB_FIRST_DWORD 0x28c00090
+
+#define LOOKUP_STUB_FIRST_DWORD 0x18000075
+
+#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)
+    {
+        // pcaddi  $r21,3
+        // ld.d  $t2,$r21,12
+        // ld.d  $r21,$r21,4
+        // jirl  $r0,$r21,0
+        //
+        // _resolveWorkerTarget
+        // _token
+
+        _stub._entryPoint[0] = LOOKUP_STUB_FIRST_DWORD; //pcaddi  $r21,3  //0x18000075
+        _stub._entryPoint[1] = 0x28c032ae; //ld.d  $t2,$r21,12
+        _stub._entryPoint[2] = 0x28c012b5; //ld.d  $r21,$r21,4
+        _stub._entryPoint[3] = 0x4c0002a0; //jirl  $r0,$r21,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[8];
+    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)
+    {
+        // pcaddi  $t4,8
+        // ld.d  $t0,$a0,0 ; methodTable from object in $a0
+        // ld.d  $r21,$t4,0    //$r21 _expectedMT
+        // bne  $r21, $t0, failLabel
+        // ld.d  $t4,$t4,8     //$t4 _implTarget
+        // jirl  $r0,$t4,0
+        // failLabel:
+        // ld.d  $t4,$t4,16    //$t4 _failTarget
+        // jirl  $r0,$t4,0
+        //
+        //
+        // _expectedMT
+        // _implTarget
+        // _failTarget
+
+        _stub._entryPoint[0] = DISPATCH_STUB_FIRST_DWORD; //pcaddi  $t4,8  //0x18000110
+        _stub._entryPoint[1] = 0x28c0008c; //ld.d  $t0,$a0,0     //; methodTable from object in $a0
+        _stub._entryPoint[2] = 0x28c00215; //ld.d  $r21,$t4,0   //$r21 _expectedMT
+        _stub._entryPoint[3] = 0x5c000eac; //bne  $r21, $t0, failLabel
+        _stub._entryPoint[4] = 0x28c02210; //ld.d  $t4,$t4,8    //$t4 _implTarget
+        _stub._entryPoint[5] = 0x4c000200; //jirl  $r0,$t4,0
+        _stub._entryPoint[6] = 0x28c04210; //ld.d  $t4,$t4,16    //$t4 _failTarget
+        _stub._entryPoint[7] = 0x4c000200; //jirl  $r0,$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 = 18;
+    const static int slowEntryPointLen = 4;
+    const static int failEntryPointLen = 9;
+
+    DWORD _resolveEntryPoint[resolveEntryPointLen];
+    DWORD _slowEntryPoint[slowEntryPointLen];
+    DWORD _failEntryPoint[failEntryPointLen];
+    UINT32  _hashedToken;
+    INT32*  _pCounter;
+    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
+        ;;t8  indirection cell
+        ;;t3  MethodTable (from object ref in a0), out: this._token
+        ;;t0  hash scratch
+        ;;t1  temp
+        ;;t2  temp
+        ;;r21 hash scratch
+        ;;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], t8, t3);
+        //    }
+        // }
+ ********/
+
+//#define PC_REL_OFFSET(_member, _index) ((((INT32)(offsetof(ResolveStub, _member) - (offsetof(ResolveStub, _resolveEntryPoint[_index]))))>>2) & 0xffff)
+
+        ///;;resolveEntryPoint
+        // Called directly by JITTED code
+        // ResolveStub._resolveEntryPoint(a0:Object*, a1 ...,a7, t8:IndirectionCellAndFlags)
+
+        //     ld.d  $t3,$a0,0
+        _stub._resolveEntryPoint[n++] = 0x28c0008f;//RESOLVE_STUB_FIRST_DWORD
+        //     srli.d  $t0,$t3,0xc
+        _stub._resolveEntryPoint[n++] = 0x004531ec;
+        //     add.d  $t1,$t3,$t0
+        _stub._resolveEntryPoint[n++] = 0x0010b1ed;
+        //     pcaddi  $t0,-3
+        _stub._resolveEntryPoint[n++] = 0x19ffffac;
+
+        //     ld.w  $r21,$t0,0  #r21 = this._hashedToken
+        _stub._resolveEntryPoint[n++] = 0x28800195 | (31<<12);//(18+4+9)*4<<10;
+        _ASSERTE((ResolveStub::resolveEntryPointLen+ResolveStub::slowEntryPointLen+ResolveStub::failEntryPointLen) == 31);
+        _ASSERTE((31<<2) == (offsetof(ResolveStub, _hashedToken) -offsetof(ResolveStub, _resolveEntryPoint[0])));
+
+        //     xor      $t1,$t1,$r21
+        _stub._resolveEntryPoint[n++] = 0x0015d5ad;
+        //     cachemask
+        _ASSERTE(CALL_STUB_CACHE_MASK * sizeof(void*) == 0x7ff8);
+        // lu12i.w  $r21,0x7ff8
+        _stub._resolveEntryPoint[n++] = 0x140fff15;
+        // srli.w  $r21,$r21,12
+        _stub._resolveEntryPoint[n++] = 0x0044b2b5;
+        //     and  $t1,$t1,$r21
+        _stub._resolveEntryPoint[n++] = 0x0014d5ad;
+        //     ld.d  $r21,$t0,0    # r21 = this._cacheAddress
+        _stub._resolveEntryPoint[n++] = 0x28c00195 | (34<<12);//(18+4+9+1+2)*4<<10;
+        _ASSERTE((ResolveStub::resolveEntryPointLen+ResolveStub::slowEntryPointLen+ResolveStub::failEntryPointLen+1+2) == 34);
+        _ASSERTE((34<<2) == (offsetof(ResolveStub, _cacheAddress) -offsetof(ResolveStub, _resolveEntryPoint[0])));
+        //     ldx.d  $t1,$r21,$t1    # t1 = e = this._cacheAddress[i]
+        _stub._resolveEntryPoint[n++] = 0x380c36ad;
+
+        //     ld.d  $r21,$t1,0  #  $r21 = Check mt == e.pMT;
+        _stub._resolveEntryPoint[n++] = 0x28c001b5 | ((offsetof(ResolveCacheElem, pMT) & 0xfff)<<10);
+        //     ld.d  $t2,$t0,0  #  $t2 = this._token
+        _stub._resolveEntryPoint[n++] = 0x28c0018e | (36<<12);//(18+4+9+1+2+2)*4<<10;
+        _ASSERTE((ResolveStub::resolveEntryPointLen+ResolveStub::slowEntryPointLen+ResolveStub::failEntryPointLen+1+4) == 36);
+        _ASSERTE((36<<2) == (offsetof(ResolveStub, _token) -offsetof(ResolveStub, _resolveEntryPoint[0])));
+
+        //     bne  $r21,$t3, next
+        _stub._resolveEntryPoint[n++] = 0x5c0016af;// | PC_REL_OFFSET(_slowEntryPoint[0], n);
+
+        //     ld.d  $r21,$t1,0      # $r21 = e.token;
+        _stub._resolveEntryPoint[n++] = 0x28c001b5 | ((offsetof(ResolveCacheElem, token) & 0xfff)<<10);
+        //     bne      $r21,$t2, next
+        _stub._resolveEntryPoint[n++] = 0x5c000eae;// | PC_REL_OFFSET(_slowEntryPoint[0], n);
+
+         pc_offset = offsetof(ResolveCacheElem, target) & 0xffffffff;
+         _ASSERTE(pc_offset >=0 && pc_offset%8 == 0);
+        //     ld.d  $t3,$t1,0     # $t3 = e.target;
+        _stub._resolveEntryPoint[n++] = 0x28c001af | ((offsetof(ResolveCacheElem, target) & 0xfff)<<10);
+        //     jirl  $r0,$t3,0
+        _stub._resolveEntryPoint[n++] = 0x4c0001e0;
+
+        _ASSERTE(n == ResolveStub::resolveEntryPointLen);
+        _ASSERTE(_stub._resolveEntryPoint + n == _stub._slowEntryPoint);
+
+        // ResolveStub._slowEntryPoint(a0:MethodToken, [a1..a7], t8:IndirectionCellAndFlags)
+        // {
+        //     t2 = this._token;
+        //     this._resolveWorkerTarget(a0, [a1..a7], t8, 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
+
+        //     pcaddi  $t0,0
+        _stub._slowEntryPoint[0] = 0x1800000c;
+        //     ld.d  $r21,$t0,0     # 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] = 0x28c00195 | 0x14000;
+
+        //     ld.d  $t2,$t0,0     # t2 = this._token;
+        _stub._slowEntryPoint[2] = 0x28c0018e | 0x12000;//(18*4=72=0x48)<<12
+        _ASSERTE((ResolveStub::slowEntryPointLen+ResolveStub::failEntryPointLen+1+4)*4 == (0x12000>>10));
+        _ASSERTE((0x12000>>10) == (offsetof(ResolveStub, _token) -offsetof(ResolveStub, _slowEntryPoint[0])));
+
+        //     jirl  $r0,$r21,0
+        _stub._slowEntryPoint[3] = 0x4c0002a0;
+
+         _ASSERTE(4 == ResolveStub::slowEntryPointLen);
+
+        // ResolveStub._failEntryPoint(a0:MethodToken, a1,.., a7, t8:IndirectionCellAndFlags)
+        // {
+        //     if(--*(this._pCounter) < 0) t8 = t8 | 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
+
+        //     pcaddi  $t0,0
+        _stub._failEntryPoint[0] = 0x1800000c;
+        //     ld.d  $t1,$t0,0     # t1 = _pCounter;  0xa000=((failEntryPointLen+1)*4)<<10.
+        _stub._failEntryPoint[1] = 0x28c0018d | 0xa000;
+        _ASSERTE((((ResolveStub::failEntryPointLen+1)*4)<<10) == 0xa000);
+        _ASSERTE((0xa000>>10) == ((INT32)(offsetof(ResolveStub, _pCounter) - (offsetof(ResolveStub, _failEntryPoint[0])))));
+        //     ld.w  $r21,$t1,0
+        _stub._failEntryPoint[2] = 0x288001b5;
+        //     addi.d  $r21,$r21,-1
+        _stub._failEntryPoint[3] = 0x02fffeb5;
+
+        //     st.w  $r21,$t1,0
+        _stub._failEntryPoint[4] = 0x298001b5;
+
+        _ASSERTE(SDF_ResolveBackPatch == 0x1);
+        // ;; ori $t8,$t8, $r21 >=0 ? SDF_ResolveBackPatch:0;
+        //     slti $r21,$r21,0
+        _stub._failEntryPoint[5] = 0x020002b5;
+        //     xori $r21,$r21,1
+        _stub._failEntryPoint[6] = 0x03c006b5;
+        //     or  $t8,$t8,$r21
+        _stub._failEntryPoint[7] = 0x00155694;
+
+        //     b       _resolveEntryPoint   //pc-120=pc+4 -resolveEntryPointLen*4 -slowEntryPointLen*4 -failEntryPointLen*4;
+        _stub._failEntryPoint[8] = 0x53ff8bff;
+
+        _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()
+    {
+        LIMITED_METHOD_CONTRACT;
+
+        BYTE* pStubCode = (BYTE *)this;
+
+
+        if ((*(DWORD*)(&pStubCode[12])) == 0x4c000200)
+        {
+            // jirl  $r0,$t4,0
+            return 20;//4*ins + slot = 4*4 + 4;
+        }
+
+        //pcaddi  $r21,0
+        assert((*(DWORD*)(&pStubCode[4])) == 0x18000015);
+
+        size_t cbSize = 32;
+
+        // ldx.d  $t4,$t4,$t3
+        if ((*(DWORD*)(&pStubCode[12])) == 0x380c3e10)
+        {
+            if ((*(DWORD*)(&pStubCode[20])) == 0x380c3e10)
+                cbSize += 8;
+        }
+
+        return cbSize;
+    }
+
+    inline PCODE        entryPoint()        const { LIMITED_METHOD_CONTRACT;  return (PCODE)&_entryPoint[0]; }
+
+    inline size_t token()
+    {
+        LIMITED_METHOD_CONTRACT;
+        DWORD slot = *(DWORD*)(reinterpret_cast<BYTE*>(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<VTableCallStub *>(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 > 2047 ? 8 : 4) + (offsetAfterIndirection > 2047 ? 8 : 4);
+        int indirectionsDataSize = (offsetOfIndirection > 2047 ? 4 : 0) + (offsetAfterIndirection > 2047 ? 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.d  $t4,$a0,0  : t4 = MethodTable pointer
+    *(UINT32*)p = 0x28c00090;//VTABLECALL_STUB_FIRST_DWORD
+    p += 4;
+
+    if ((offsetOfIndirection > 2047) || (offsetAfterIndirection > 2047))
+    {
+        *(UINT32*)p = 0x18000015;//pcaddi  $r21,0
+        p += 4;
+    }
+
+    if (offsetOfIndirection > 2047)
+    {
+        uint dataOffset = 16 + (offsetAfterIndirection > 2047 ? 8 : 4);
+
+        // ld.wu  $t3,$r21,dataOffset
+        *(DWORD*)p = 0x2a8002af | ((UINT32)dataOffset << 10); p += 4;
+        // ldx.d  $t4,$t4,$t3
+        *(DWORD*)p = 0x380c3e10; p += 4;
+    }
+    else
+    {
+        // ld.d  $t4,$t4,offsetOfIndirection
+        *(DWORD*)p = 0x28c00210 | ((UINT32)offsetOfIndirection << 10); p += 4;
+    }
+
+    if (offsetAfterIndirection > 2047)
+    {
+        uint indirectionsCodeSize = (offsetOfIndirection > 2047 ? 8 : 4);
+        uint indirectionsDataSize = (offsetOfIndirection > 2047 ? 4 : 0);
+        uint dataOffset = 16 + indirectionsCodeSize + indirectionsDataSize;
+
+        // ld.wu  $t3,$r21,dataOffset
+        *(DWORD*)p = 0x2a8002af | ((UINT32)dataOffset << 10); p += 4;
+        // ldx.d  $t4,$t4,$t3
+        *(DWORD*)p = 0x380c3e10; p += 4;
+    }
+    else
+    {
+        // ld.d  $t4,$t4,offsetAfterIndirection
+        *(DWORD*)p = 0x28c00210 | ((UINT32)offsetAfterIndirection << 10); p += 4;
+    }
+
+    // jirl  $r0,$t4,0
+    *(UINT32*)p = 0x4c000200; p += 4;
+
+    // data labels:
+    if (offsetOfIndirection > 2047)
+    {
+        *(UINT32*)p = (UINT32)offsetOfIndirection;
+        p += 4;
+    }
+    if (offsetAfterIndirection > 2047)
+    {
+        *(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_LOONGARCH64_H
index 18ed5e2..88b02f1 100644 (file)
@@ -2881,6 +2881,463 @@ void  MethodTable::AssignClassifiedEightByteTypes(SystemVStructRegisterPassingHe
 
 #endif // defined(UNIX_AMD64_ABI_ITF)
 
+#if defined(TARGET_LOONGARCH64)
+int MethodTable::GetLoongArch64PassStructInRegisterFlags(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->GetFieldTypeHandleThrowing().GetMethodTable();
+                    if (pMethodTable->GetNumIntroducedInstanceFields() == 1)
+                    {
+                        size = GetLoongArch64PassStructInRegisterFlags((CORINFO_CLASS_HANDLE)pMethodTable);
+                    }
+                    else if (pMethodTable->GetNumIntroducedInstanceFields() == 2)
+                    {
+                        size = GetLoongArch64PassStructInRegisterFlags((CORINFO_CLASS_HANDLE)pMethodTable);
+                    }
+                }
+            }
+            else if (numIntroducedFields == 2)
+            {
+                FieldDesc *pFieldStart = pMethodTable->GetApproxFieldDescListRaw();
+                if (pFieldStart->GetSize() > 8)
+                    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;
+
+                }
+                else if (fieldType == ELEMENT_TYPE_VALUETYPE)
+                {
+                    pMethodTable  = pFieldStart->GetFieldTypeHandleThrowing().GetMethodTable();
+                    if (pMethodTable->GetNumIntroducedInstanceFields() == 1)
+                    {
+                        size = GetLoongArch64PassStructInRegisterFlags((CORINFO_CLASS_HANDLE)pMethodTable);
+                        if ((size & STRUCT_FLOAT_FIELD_ONLY_ONE) != 0)
+                        {
+                            size = pFieldStart[0].GetSize() == 8 ? STRUCT_FIRST_FIELD_DOUBLE : STRUCT_FLOAT_FIELD_FIRST;
+                        }
+                        else if (size == STRUCT_NO_FLOAT_FIELD)
+                        {
+                            size = pFieldStart[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 (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)
+                {
+                    pMethodTable  = pFieldStart[1].GetFieldTypeHandleThrowing().GetMethodTable();
+                    if (pMethodTable->GetNumIntroducedInstanceFields() == 1)
+                    {
+                        int size2 = GetLoongArch64PassStructInRegisterFlags((CORINFO_CLASS_HANDLE)pMethodTable);
+                        if ((size2 & STRUCT_FLOAT_FIELD_ONLY_ONE) != 0)
+                        {
+                            if (pFieldStart[1].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 |= pFieldStart[1].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 (pFieldStart[1].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 = GetLoongArch64PassStructInRegisterFlags((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();
+
+                        size = GetLoongArch64PassStructInRegisterFlags((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 (fieldType == ELEMENT_TYPE_CLASS)
+                {
+                    MethodTable* pMethodTable2  = pFieldStart->GetFieldTypeHandleThrowing().GetMethodTable();
+
+                    if (pMethodTable2->IsArray())
+                    {
+                        // Here is just skip the array as its elements' count greater than 1.
+                        // TODO-LoongArch64: liking `struct {int array[1]; float field_2;}` which the array field is only one element,
+                        // this struct can be passed by registers and should using float-register.
+                        // Details see github https://github.com/dotnet/runtime/pull/62885#discussion_r821878981
+                        size = STRUCT_NO_FLOAT_FIELD;
+                        goto _End_arg;
+                    }
+                    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)
+                {
+                    MethodTable* pMethodTable2 = pFieldStart[1].GetFieldTypeHandleThrowing().AsMethodTable();
+
+                    if (pMethodTable2->GetNumIntroducedInstanceFields() > 1)
+                    {
+                        size = STRUCT_NO_FLOAT_FIELD;
+                        goto _End_arg;
+                    }
+
+                    if (pMethodTable2->HasLayout())
+                    {
+                        const NativeFieldDescriptor *pNativeFieldDescs = pMethodTable2->GetNativeLayoutInfo()->GetNativeFieldDescriptors();
+
+                        NativeFieldCategory nfc = pNativeFieldDescs->GetCategory();
+                        if (nfc == NativeFieldCategory::NESTED)
+                        {
+                            pMethodTable = pNativeFieldDescs->GetNestedNativeMethodTable();
+
+                            if (pMethodTable->GetNumIntroducedInstanceFields() > 1)
+                            {
+                                size = STRUCT_NO_FLOAT_FIELD;
+                                goto _End_arg;
+                            }
+
+                            if ((GetLoongArch64PassStructInRegisterFlags((CORINFO_CLASS_HANDLE)pMethodTable) & 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 ((size & STRUCT_FLOAT_FIELD_FIRST) == 0)
+                            {
+                                size = STRUCT_NO_FLOAT_FIELD;
+                                goto _End_arg;
+                            }
+                            else if (pFieldStart[1].GetSize() == 8)
+                                size |= STRUCT_SECOND_FIELD_SIZE_IS8;
+                        }
+                    }
+                    else
+                    {
+                        if ((GetLoongArch64PassStructInRegisterFlags((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 (fieldType == ELEMENT_TYPE_CLASS)
+                {
+                    MethodTable* pMethodTable2  = pFieldStart[1].GetFieldTypeHandleThrowing().GetMethodTable();
+
+                    if (pMethodTable2->IsArray())
+                    {
+                        // Here is just skip the array as its elements' count greater than 1.
+                        // TODO-LoongArch64: liking `struct {int array[1]; float field_2;}` which the array field is only one element,
+                        // this struct can be passed by registers and should using float-register.
+                        // Details see github https://github.com/dotnet/runtime/pull/62885#discussion_r821878981
+                        size = STRUCT_NO_FLOAT_FIELD;
+                        goto _End_arg;
+                    }
+                    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()
index 039b5c0..38e2d9c 100644 (file)
@@ -749,6 +749,10 @@ public:
     // during object construction.
     void CheckRunClassInitAsIfConstructingThrowing();
 
+#if defined(TARGET_LOONGARCH64)
+    static int GetLoongArch64PassStructInRegisterFlags(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);
index c52c46d..451bb1e 100644 (file)
@@ -394,6 +394,14 @@ void STDCALL CopyValueClassArgUnchecked(ArgDestination *argDest, void* src, Meth
         return;
     }
 
+#elif defined(TARGET_LOONGARCH64)
+
+    if (argDest->IsStructPassedInRegs())
+    {
+        argDest->CopyStructToRegisters(src, pMT->GetNumInstanceFieldBytes());
+        return;
+    }
+
 #endif // UNIX_AMD64_ABI
     // destOffset is only valid for Nullable<T> passed in registers
     _ASSERTE(destOffset == 0);
@@ -418,6 +426,16 @@ void InitValueClassArg(ArgDestination *argDest, MethodTable *pMT)
     }
 
 #endif
+
+#if defined(TARGET_LOONGARCH64)
+    if (argDest->IsStructPassedInRegs())
+    {
+        *(UINT64*)(argDest->GetStructGenRegDestinationAddress()) = 0;
+        *(UINT64*)(argDest->GetDestinationAddress()) = 0;
+        return;
+    }
+#endif
+
     InitValueClass(argDest->GetDestinationAddress(), pMT);
 }
 
@@ -1790,6 +1808,44 @@ BOOL Nullable::UnBoxIntoArgNoGC(ArgDestination *argDest, OBJECTREF boxedVal, Met
 
 #endif // UNIX_AMD64_ABI
 
+#if defined(TARGET_LOONGARCH64)
+    if (argDest->IsStructPassedInRegs())
+    {
+        // We should only get here if we are unboxing a T as a Nullable<T>
+        _ASSERTE(IsNullableType(destMT));
+
+        // We better have a concrete instantiation, or our field offset asserts are not useful
+        _ASSERTE(!destMT->ContainsGenericVariables());
+
+        if (boxedVal == NULL)
+        {
+            // Logically we are doing *dest->HasValueAddr(destMT) = false;
+            // We zero out the whole structure becasue it may contain GC references
+            // and these need to be initialized to zero.   (could optimize in the non-GC case)
+            InitValueClassArg(argDest, destMT);
+        }
+        else
+        {
+            if (!IsNullableForTypeNoGC(destMT, boxedVal->GetMethodTable()))
+            {
+                // For safety's sake, also allow true nullables to be unboxed normally.
+                // This should not happen normally, but we want to be robust
+                if (destMT == boxedVal->GetMethodTable())
+                {
+                    CopyValueClassArg(argDest, boxedVal->GetData(), destMT, 0);
+                    return TRUE;
+                }
+                return FALSE;
+            }
+
+            CopyValueClassArg(argDest, boxedVal->UnBox(), boxedVal->GetMethodTable(), 0);
+            *(UINT64*)(argDest->GetStructGenRegDestinationAddress()) = 1;
+        }
+        return TRUE;
+    }
+
+#endif
+
     return UnBoxNoGC(argDest->GetDestinationAddress(), boxedVal, destMT);
 }
 
index ad95a53..90a290a 100644 (file)
@@ -614,7 +614,12 @@ void StubPrecode::StaticInitialize()
 #else
     _ASSERTE(((BYTE*)StubPrecodeCode_End - (BYTE*)StubPrecodeCode) <= StubPrecode::CodeSize);
 #endif
+#ifdef TARGET_LOONGARCH64
+    _ASSERTE(((*((short*)PCODEToPINSTR((PCODE)StubPrecodeCode) + OFFSETOF_PRECODE_TYPE)) >> 5) == StubPrecode::Type);
+#else
     _ASSERTE((*((BYTE*)PCODEToPINSTR((PCODE)StubPrecodeCode) + OFFSETOF_PRECODE_TYPE)) == StubPrecode::Type);
+#endif
+
 }
 
 void StubPrecode::GenerateCodePage(BYTE* pageBase, BYTE* pageBaseRX)
@@ -721,7 +726,11 @@ void FixupPrecode::StaticInitialize()
 #else
     _ASSERTE((BYTE*)FixupPrecodeCode_End - (BYTE*)FixupPrecodeCode <= FixupPrecode::CodeSize);
 #endif
+#ifdef TARGET_LOONGARCH64
+    _ASSERTE(((*((short*)PCODEToPINSTR((PCODE)StubPrecodeCode) + OFFSETOF_PRECODE_TYPE)) >> 5) == StubPrecode::Type);
+#else
     _ASSERTE(*((BYTE*)PCODEToPINSTR((PCODE)FixupPrecodeCode) + OFFSETOF_PRECODE_TYPE) == FixupPrecode::Type);
+#endif
 }
 
 void FixupPrecode::GenerateCodePage(BYTE* pageBase, BYTE* pageBaseRX)
index da1dfd5..d4d02d4 100644 (file)
@@ -39,6 +39,11 @@ EXTERN_C VOID STDCALL PrecodeRemotingThunk();
 #define SIZEOF_PRECODE_BASE         CODE_SIZE_ALIGN
 #define OFFSETOF_PRECODE_TYPE       3
 
+#elif defined(HOST_LOONGARCH64)
+
+#define SIZEOF_PRECODE_BASE         CODE_SIZE_ALIGN
+#define OFFSETOF_PRECODE_TYPE       0
+
 #endif // HOST_AMD64
 
 #ifndef DACCESS_COMPILE
@@ -56,6 +61,8 @@ struct InvalidPrecode
     static const int Type = 0xCC;
 #elif defined(HOST_ARM64) || defined(HOST_ARM)
     static const int Type = 0;
+#elif defined(HOST_LOONGARCH64)
+    static const int Type = 0xff;
 #endif
 };
 
@@ -88,6 +95,9 @@ struct StubPrecode
 #elif defined(HOST_ARM)
     static const int Type = 0xCF;
     static const int CodeSize = 12;
+#elif defined(HOST_LOONGARCH64)
+    static const int Type = 0x4;
+    static const int CodeSize = 24;
 #endif // HOST_AMD64
 
     BYTE m_code[CodeSize];
@@ -220,6 +230,10 @@ struct FixupPrecode
     static const int Type = 0xFF;
     static const int CodeSize = 12;
     static const int FixupCodeOffset = 4 + THUMB_CODE;
+#elif defined(HOST_LOONGARCH64)
+    static const int Type = 0x3;
+    static const int CodeSize = 32;
+    static const int FixupCodeOffset = 12;
 #endif // HOST_AMD64
 
     BYTE m_code[CodeSize];
@@ -411,7 +425,14 @@ public:
 
 #ifdef OFFSETOF_PRECODE_TYPE
 
+#ifdef TARGET_LOONGARCH64
+        assert(0 == OFFSETOF_PRECODE_TYPE);
+        short type = *((short*)m_data);
+        type >>= 5;
+#else
         BYTE type = m_data[OFFSETOF_PRECODE_TYPE];
+#endif
+
         if (type == StubPrecode::Type)
         {
             // StubPrecode code is used for both StubPrecode and NDirectImportPrecode,
index df1178b..089c273 100644 (file)
@@ -2291,7 +2291,7 @@ PCODE TheVarargNDirectStub(BOOL hasRetBuffArg)
 {
     LIMITED_METHOD_CONTRACT;
 
-#if !defined(TARGET_X86) && !defined(TARGET_ARM64)
+#if !defined(TARGET_X86) && !defined(TARGET_ARM64) && !defined(TARGET_LOONGARCH64)
     if (hasRetBuffArg)
     {
         return GetEEFuncEntryPoint(VarargPInvokeStub_RetBuffArg);
index 18345ae..47d653c 100644 (file)
@@ -303,7 +303,7 @@ bool CrawlFrame::IsGcSafe()
     return GetCodeManager()->IsGcSafe(&codeInfo, GetRelOffset());
 }
 
-#if defined(TARGET_ARM) || defined(TARGET_ARM64)
+#if defined(TARGET_ARM) || defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64)
 bool CrawlFrame::HasTailCalls()
 {
     CONTRACTL {
@@ -314,7 +314,7 @@ bool CrawlFrame::HasTailCalls()
 
     return GetCodeManager()->HasTailCalls(&codeInfo);
 }
-#endif // TARGET_ARM || TARGET_ARM64
+#endif // TARGET_ARM || TARGET_ARM64 || TARGET_LOONGARCH64
 
 inline void CrawlFrame::GotoNextFrame()
 {
@@ -640,6 +640,9 @@ PCODE Thread::VirtualUnwindLeafCallFrame(T_CONTEXT* pContext)
 
     uControlPc = TADDR(pContext->Lr);
 
+#elif defined(TARGET_LOONGARCH64)
+    uControlPc = TADDR(pContext->Ra);
+
 #else
     PORTABILITY_ASSERT("Thread::VirtualUnwindLeafCallFrame");
     uControlPc = NULL;
index 52ece57..dcdd0bc 100644 (file)
@@ -305,9 +305,9 @@ public:
      */
     bool IsGcSafe();
 
-#if defined(TARGET_ARM) || defined(TARGET_ARM64)
+#if defined(TARGET_ARM) || defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64)
     bool HasTailCalls();
-#endif // TARGET_ARM || TARGET_ARM64
+#endif // TARGET_ARM || TARGET_ARM64 || TARGET_LOONGARCH64
 
     PREGDISPLAY GetRegisterSet()
     {
index 60fffe5..9efe9c7 100644 (file)
@@ -1828,7 +1828,7 @@ static BOOL IsVarargPInvokeStub(PCODE stubStartAddress)
     if (stubStartAddress == GetEEFuncEntryPoint(VarargPInvokeStub))
         return TRUE;
 
-#if !defined(TARGET_X86) && !defined(TARGET_ARM64)
+#if !defined(TARGET_X86) && !defined(TARGET_ARM64) && !defined(TARGET_LOONGARCH64)
     if (stubStartAddress == GetEEFuncEntryPoint(VarargPInvokeStub_RetBuffArg))
         return TRUE;
 #endif
index c7e1653..1623c2d 100644 (file)
@@ -1122,24 +1122,19 @@ PCODE AdjustWriteBarrierIP(PCODE controlPc)
 
 #ifdef TARGET_X86
 extern "C" void *JIT_WriteBarrierEAX_Loc;
+#elif TARGET_AMD64
+extern "C" void *JIT_WriteBarrier_Loc;
 #else
 extern "C" void *JIT_WriteBarrier_Loc;
+void *JIT_WriteBarrier_Loc = 0;
 #endif
 
 #ifdef TARGET_ARM64
 extern "C" void (*JIT_WriteBarrier_Table)();
-
-extern "C" void *JIT_WriteBarrier_Loc;
-void *JIT_WriteBarrier_Loc = 0;
-
 extern "C" void *JIT_WriteBarrier_Table_Loc;
 void *JIT_WriteBarrier_Table_Loc = 0;
 #endif // TARGET_ARM64
 
-#ifdef TARGET_ARM
-extern "C" void *JIT_WriteBarrier_Loc = 0;
-#endif // TARGET_ARM
-
 #ifndef TARGET_UNIX
 // g_TlsIndex is only used by the DAC. Disable optimizations around it to prevent it from getting optimized out.
 #pragma optimize("", off)
index f9daa20..f2fa3a0 100644 (file)
@@ -3701,7 +3701,7 @@ void Thread::CommitGCStressInstructionUpdate()
         else
             *(DWORD*)destCodeWriterHolder.GetRW() = *(DWORD*)pbSrcCode;
 
-#elif defined(TARGET_ARM64)
+#elif defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64)
 
         *(DWORD*)destCodeWriterHolder.GetRW() = *(DWORD*)pbSrcCode;
 
@@ -4860,7 +4860,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)
+#if defined(TARGET_ARM) || defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64)
 
                         // Why do we use CallerContextPointers below?
                         //
@@ -4879,7 +4879,11 @@ 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 (pRDT->pCallerContextPointers->Ra == &pRDT->pContext->Ra)
+#else
                         if(pRDT->pCallerContextPointers->Lr == &pRDT->pContext->Lr)
+#endif
                         {
                             // This is the case when we are either:
                             //
@@ -4914,7 +4918,11 @@ 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)
+                            pES->m_ppvRetAddrPtr = (void **) pRDT->pCallerContextPointers->Ra;
+#else
                             pES->m_ppvRetAddrPtr = (void **) pRDT->pCallerContextPointers->Lr;
+#endif
                         }
 #elif defined(TARGET_X86) || defined(TARGET_AMD64)
                         pES->m_ppvRetAddrPtr = (void **) (EECodeManager::GetCallerSp(pRDT) - sizeof(void*));