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_
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)
/*
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)
/*
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)
#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
}
}
+ if (targetArch == SPMI_TARGET_ARCHITECTURE_LOONGARCH64)
+ {
+ Assert(!"FIXME: Not Implements on loongarch64");
+ }
+
if (IsSpmiTarget64Bit())
{
if (relocType == IMAGE_REL_BASED_DIR64)
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
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();
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()
{
SetSpmiTargetArchitecture(SPMI_TARGET_ARCHITECTURE_ARM64);
}
+ else if (0 == _stricmp(targetArchitecture, "loongarch64"))
+ {
+ SetSpmiTargetArchitecture(SPMI_TARGET_ARCHITECTURE_LOONGARCH64);
+ }
else
{
LogError("Illegal target architecture '%s'", targetArchitecture);
${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)
${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)
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);
#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.
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:
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];
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)
#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
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
#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;
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;
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;
}
#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
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
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,
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
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
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;
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:
}
#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
#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;
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);
//-----------------------------------------------------------------------------
-#if defined(TARGET_ARM) || defined(TARGET_ARM64)
+#if defined(TARGET_ARM) || defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64)
#define EXCEPTION_DATA_SUPPORTS_FUNCTION_FRAGMENTS
#endif
}
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
*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;
}
#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
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
{
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.
// 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()
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
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
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
// 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++;
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)
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 {
return gcInfoDecoder.HasTailCalls();
}
-#endif // TARGET_ARM || TARGET_ARM64
+#endif // TARGET_ARM || TARGET_ARM64 || TARGET_LOONGARCH64
#if defined(TARGET_AMD64) && defined(_DEBUG)
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, ",
#endif // TARGET_UNIX
-#ifndef TARGET_ARM64
+#if !defined(TARGET_ARM64) && !defined(TARGET_LOONGARCH64)
EXTERN_C void JIT_StackProbe_End();
#endif // TARGET_ARM64
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
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:
// 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
#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
//
// 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
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
// 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;
}
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.
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
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;
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.
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
#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);
#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
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
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
{
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);
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);
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,
{
*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
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);
#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
{
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
// 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_
}
}
instrPtrRW += instrLen;
-#elif defined(TARGET_ARM64)
+#elif defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64)
*((DWORD*)instrPtrRW) = INTERRUPT_INSTR;
instrPtrRW += 4;
#endif // TARGET_XXXX_
{
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
*(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
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))
}
#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
*(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
retValRegs[numberOfRegs++] = regs->R0;
#elif defined(TARGET_ARM64)
retValRegs[numberOfRegs++] = regs->X0;
+#elif defined(TARGET_LOONGARCH64)
+ retValRegs[numberOfRegs++] = regs->A0;
#endif // TARGET_ARM64
}
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
#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
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);
// 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.
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);
// (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)
{
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;
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
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
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
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()
{
}
#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(
int esp = 13;
#elif defined(TARGET_ARM64)
int esp = 31;
+#elif defined(TARGET_LOONGARCH64)
+ int esp = 3;
#endif
if( GC_SP_REL == spBase )
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;
}
#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*))
{
// 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());
}
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
// x8 through x15 are scratch registers on ARM64.
IntReg x8 = IntReg(8);
IntReg x9 = IntReg(9);
-#else
#error unsupported platform
#endif
}
#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
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
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
#endif
);
}
+#elif defined(HOST_LOONGARCH64)
+ argState.AddArg(k, static_cast<short>(szSlots));
#else
#error unknown platform
#endif
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
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
sl.EmitEpilog();
+#elif defined(HOST_LOONGARCH64)
+ assert(!"unimplemented on LOONGARCH yet");
#else
#error unsupported platform
#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
unsigned totalArgSlots = nSlots + HFAReturnArgSlots;
#elif defined(HOST_AMD64)
unsigned totalArgSlots = nSlots;
+#elif defined(HOST_LOONGARCH64)
+ unsigned totalArgSlots = nSlots;
#else
#error "unsupported platform"
#endif
#else
static const int MaxNumFPRegArgSlots = 4;
#endif
+#elif defined(HOST_LOONGARCH64)
+ static const int MaxNumFPRegArgSlots = 8;
#endif
~ArgState()
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
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();
// 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)
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();
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
};
--- /dev/null
+// 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
--- /dev/null
+// 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
--- /dev/null
+// 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
--- /dev/null
+// 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__
--- /dev/null
+// 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
--- /dev/null
+// 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__
--- /dev/null
+// 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 *)©->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
--- /dev/null
+// 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
--- /dev/null
+// 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
--- /dev/null
+// 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
--- /dev/null
+// 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
--- /dev/null
+// 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");
+ }
+};
--- /dev/null
+// 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
#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()
// 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);
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);
}
#endif
+
+#if defined(TARGET_LOONGARCH64)
+ if (argDest->IsStructPassedInRegs())
+ {
+ *(UINT64*)(argDest->GetStructGenRegDestinationAddress()) = 0;
+ *(UINT64*)(argDest->GetDestinationAddress()) = 0;
+ return;
+ }
+#endif
+
InitValueClass(argDest->GetDestinationAddress(), pMT);
}
#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);
}
#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)
#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)
#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
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
};
#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];
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];
#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,
{
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);
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 {
return GetCodeManager()->HasTailCalls(&codeInfo);
}
-#endif // TARGET_ARM || TARGET_ARM64
+#endif // TARGET_ARM || TARGET_ARM64 || TARGET_LOONGARCH64
inline void CrawlFrame::GotoNextFrame()
{
uControlPc = TADDR(pContext->Lr);
+#elif defined(TARGET_LOONGARCH64)
+ uControlPc = TADDR(pContext->Ra);
+
#else
PORTABILITY_ASSERT("Thread::VirtualUnwindLeafCallFrame");
uControlPc = NULL;
*/
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()
{
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
#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)
else
*(DWORD*)destCodeWriterHolder.GetRW() = *(DWORD*)pbSrcCode;
-#elif defined(TARGET_ARM64)
+#elif defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64)
*(DWORD*)destCodeWriterHolder.GetRW() = *(DWORD*)pbSrcCode;
{
// 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?
//
// 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:
//
// 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*));