summaryrefslogtreecommitdiff
path: root/src/vm/arm64/stubs.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/vm/arm64/stubs.cpp')
-rw-r--r--src/vm/arm64/stubs.cpp2167
1 files changed, 2167 insertions, 0 deletions
diff --git a/src/vm/arm64/stubs.cpp b/src/vm/arm64/stubs.cpp
new file mode 100644
index 0000000000..9c9b6a8a68
--- /dev/null
+++ b/src/vm/arm64/stubs.cpp
@@ -0,0 +1,2167 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+//
+// File: stubs.cpp
+//
+// This file contains stub functions for unimplemented features need to
+// run on the ARM64 platform.
+
+#include "common.h"
+#include "dllimportcallback.h"
+#include "comdelegate.h"
+#include "tls.h"
+#include "asmconstants.h"
+#include "virtualcallstub.h"
+#include "jitinterface.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(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 || "ARM64:NYI - CompareAndBranchInstructionFormat::CanReach external");
+ if (fExternal)
+ return false;
+
+ if (offset < -1048576 || offset > 1048572)
+ return false;
+ return true;
+ }
+ // 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 *pOutBuffer, UINT variationCode, BYTE *pDataBuffer)
+ {
+ LIMITED_METHOD_CONTRACT;
+
+ _ASSERTE(refSize == InstructionFormat::k32);
+
+ if (fixedUpReference < -1048576 || fixedUpReference > 1048572)
+ COMPlusThrow(kNotSupportedException);
+
+ _ASSERTE((fixedUpReference & 0x3) == 0);
+ DWORD imm19 = (DWORD)(0x7FFFF & (fixedUpReference >> 2));
+
+ pOutBuffer[0] = static_cast<BYTE>((0x7 & imm19 /* Bits2-0(imm19) */) << 5 | (0xF & variationCode /* cond */));
+ pOutBuffer[1] = static_cast<BYTE>((0x7F8 & imm19 /* Bits10-3(imm19) */) >> 3);
+ pOutBuffer[2] = static_cast<BYTE>((0x7F800 & imm19 /* Bits19-11(imm19) */) >> 11);
+ pOutBuffer[3] = static_cast<BYTE>(0x54);
+ }
+};
+
+//-----------------------------------------------------------------------
+// InstructionFormat for B(L)(R) (unconditional branch)
+//-----------------------------------------------------------------------
+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- (BL(R)) or not (B(R))
+
+ 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 12;
+ else
+ return 8;
+ }
+
+ 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 >= -134217728 && offset <= 134217724) || (refSize == InstructionFormat::k64));
+ }
+ }
+
+ virtual VOID EmitInstruction(UINT refSize, __int64 fixedUpReference, BYTE *pOutBuffer, UINT variationCode, BYTE *pDataBuffer)
+ {
+ LIMITED_METHOD_CONTRACT;
+
+ if (IsIndirect(variationCode))
+ {
+ _ASSERTE(((UINT_PTR)pDataBuffer & 7) == 0);
+ __int64 dataOffset = pDataBuffer - pOutBuffer;
+
+ if (dataOffset < -1048576 || dataOffset > 1048572)
+ COMPlusThrow(kNotSupportedException);
+
+ DWORD imm19 = (DWORD)(0x7FFFF & (dataOffset >> 2));
+
+ // +0: ldr x16, [pc, #dataOffset]
+ // +4: ldr x16, [x16]
+ // +8: b(l)r x16
+ *((DWORD*)pOutBuffer) = (0x58000010 | (imm19 << 5));
+ *((DWORD*)(pOutBuffer+4)) = 0xF9400210;
+ if (IsCall(variationCode))
+ {
+ *((DWORD*)(pOutBuffer+8)) = 0xD63F0200; // blr x16
+ }
+ else
+ {
+ *((DWORD*)(pOutBuffer+8)) = 0xD61F0200; // br x16
+ }
+
+
+ *((__int64*)pDataBuffer) = fixedUpReference + (__int64)pOutBuffer;
+ }
+ else
+ {
+
+ _ASSERTE(((UINT_PTR)pDataBuffer & 7) == 0);
+ __int64 dataOffset = pDataBuffer - pOutBuffer;
+
+ if (dataOffset < -1048576 || dataOffset > 1048572)
+ COMPlusThrow(kNotSupportedException);
+
+ DWORD imm19 = (DWORD)(0x7FFFF & (dataOffset >> 2));
+
+ // +0: ldr x16, [pc, #dataOffset]
+ // +4: b(l)r x16
+ *((DWORD*)pOutBuffer) = (0x58000010 | (imm19 << 5));
+ if (IsCall(variationCode))
+ {
+ *((DWORD*)(pOutBuffer+4)) = 0xD63F0200; // blr x16
+ }
+ else
+ {
+ *((DWORD*)(pOutBuffer+4)) = 0xD61F0200; // br x16
+ }
+
+ if (!ClrSafeInt<__int64>::addition(fixedUpReference, (__int64)pOutBuffer, fixedUpReference))
+ COMPlusThrowArithmetic();
+ *((__int64*)pDataBuffer) = fixedUpReference;
+ }
+ }
+
+};
+
+//-----------------------------------------------------------------------
+// InstructionFormat for loading a label to the register (ADRP/ADR)
+//-----------------------------------------------------------------------
+class LoadFromLabelInstructionFormat : public InstructionFormat
+{
+ public:
+ LoadFromLabelInstructionFormat() : InstructionFormat( InstructionFormat::k32)
+ {
+ LIMITED_METHOD_CONTRACT;
+ }
+
+ virtual UINT GetSizeOfInstruction(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)
+ {
+ return fExternal;
+ }
+
+ virtual VOID EmitInstruction(UINT refSize, __int64 fixedUpReference, BYTE *pOutBuffer, UINT variationCode, BYTE *pDataBuffer)
+ {
+ 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);
+
+ // Can't use SP or XZR
+ _ASSERTE((variationCode & 0x1F) != 31);
+
+ // adrp Xt, #Page_of_fixedUpReference
+ *((DWORD*)pOutBuffer) = ((9<<28) | ((imm & 3)<<29) | (imm>>2)<<5 | (variationCode&0x1F));
+
+ // ldr Xt, [Xt, #offset_of_fixedUpReference_to_its_page]
+ UINT64 target = (UINT64)(fixedUpReference + pOutBuffer)>>3;
+ *((DWORD*)(pOutBuffer+4)) = ( 0xF9400000 | ((target & 0x1FF)<<10) | (variationCode & 0x1F)<<5 | (variationCode & 0x1F));
+ }
+};
+
+
+
+static BYTE gConditionalBranchIF[sizeof(ConditionalBranchInstructionFormat)];
+static BYTE gBranchIF[sizeof(BranchInstructionFormat)];
+static BYTE gLoadFromLabelIF[sizeof(LoadFromLabelInstructionFormat)];
+
+#endif
+
+#ifndef CROSSGEN_COMPILE
+void LazyMachState::unwindLazyState(LazyMachState* baseState,
+ MachState* unwoundstate,
+ DWORD threadId,
+ int funCallDepth,
+ HostCallPreference hostCallPreference)
+{
+ T_CONTEXT context;
+ T_KNONVOLATILE_CONTEXT_POINTERS nonVolContextPtrs;
+
+ context.X19 = unwoundstate->captureX19_X29[0] = baseState->captureX19_X29[0];
+ context.X20 = unwoundstate->captureX19_X29[1] = baseState->captureX19_X29[1];
+ context.X21 = unwoundstate->captureX19_X29[2] = baseState->captureX19_X29[2];
+ context.X22 = unwoundstate->captureX19_X29[3] = baseState->captureX19_X29[3];
+ context.X23 = unwoundstate->captureX19_X29[4] = baseState->captureX19_X29[4];
+ context.X24 = unwoundstate->captureX19_X29[5] = baseState->captureX19_X29[5];
+ context.X25 = unwoundstate->captureX19_X29[6] = baseState->captureX19_X29[6];
+ context.X26 = unwoundstate->captureX19_X29[7] = baseState->captureX19_X29[7];
+ context.X27 = unwoundstate->captureX19_X29[8] = baseState->captureX19_X29[8];
+ context.X28 = unwoundstate->captureX19_X29[9] = baseState->captureX19_X29[9];
+ context.Fp = unwoundstate->captureX19_X29[10] = baseState->captureX19_X29[10];
+ context.Lr = 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.X19 = &unwoundstate->captureX19_X29[0];
+ nonVolContextPtrs.X20 = &unwoundstate->captureX19_X29[1];
+ nonVolContextPtrs.X21 = &unwoundstate->captureX19_X29[2];
+ nonVolContextPtrs.X22 = &unwoundstate->captureX19_X29[3];
+ nonVolContextPtrs.X23 = &unwoundstate->captureX19_X29[4];
+ nonVolContextPtrs.X24 = &unwoundstate->captureX19_X29[5];
+ nonVolContextPtrs.X25 = &unwoundstate->captureX19_X29[6];
+ nonVolContextPtrs.X26 = &unwoundstate->captureX19_X29[7];
+ nonVolContextPtrs.X27 = &unwoundstate->captureX19_X29[8];
+ nonVolContextPtrs.X28 = &unwoundstate->captureX19_X29[9];
+ nonVolContextPtrs.Fp = &unwoundstate->captureX19_X29[10];
+ nonVolContextPtrs.Lr = 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 FEATURE_PAL
+ pvControlPc = Thread::VirtualUnwindCallFrame(&context, &nonVolContextPtrs);
+#else // !FEATURE_PAL
+#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 // !FEATURE_PAL
+
+ 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 DACCESS_COMPILE
+ // For DAC builds, we update the registers directly since we dont have context pointers
+ unwoundstate->captureX19_X29[0] = context.X19;
+ unwoundstate->captureX19_X29[1] = context.X20;
+ unwoundstate->captureX19_X29[2] = context.X21;
+ unwoundstate->captureX19_X29[3] = context.X22;
+ unwoundstate->captureX19_X29[4] = context.X23;
+ unwoundstate->captureX19_X29[5] = context.X24;
+ unwoundstate->captureX19_X29[6] = context.X25;
+ unwoundstate->captureX19_X29[7] = context.X26;
+ unwoundstate->captureX19_X29[8] = context.X27;
+ unwoundstate->captureX19_X29[9] = context.X28;
+ unwoundstate->captureX19_X29[10] = context.Fp;
+#else // !DACCESS_COMPILE
+ // For non-DAC builds, update the register state from context pointers
+ unwoundstate->ptrX19_X29[0] = nonVolContextPtrs.X19;
+ unwoundstate->ptrX19_X29[1] = nonVolContextPtrs.X20;
+ unwoundstate->ptrX19_X29[2] = nonVolContextPtrs.X21;
+ unwoundstate->ptrX19_X29[3] = nonVolContextPtrs.X22;
+ unwoundstate->ptrX19_X29[4] = nonVolContextPtrs.X23;
+ unwoundstate->ptrX19_X29[5] = nonVolContextPtrs.X24;
+ unwoundstate->ptrX19_X29[6] = nonVolContextPtrs.X25;
+ unwoundstate->ptrX19_X29[7] = nonVolContextPtrs.X26;
+ unwoundstate->ptrX19_X29[8] = nonVolContextPtrs.X27;
+ unwoundstate->ptrX19_X29[9] = nonVolContextPtrs.X28;
+ unwoundstate->ptrX19_X29[10] = nonVolContextPtrs.Fp;
+#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->X19 = (DWORD64)(pUnwoundState->captureX19_X29[0]);
+ pRD->pCurrentContext->X20 = (DWORD64)(pUnwoundState->captureX19_X29[1]);
+ pRD->pCurrentContext->X21 = (DWORD64)(pUnwoundState->captureX19_X29[2]);
+ pRD->pCurrentContext->X22 = (DWORD64)(pUnwoundState->captureX19_X29[3]);
+ pRD->pCurrentContext->X23 = (DWORD64)(pUnwoundState->captureX19_X29[4]);
+ pRD->pCurrentContext->X24 = (DWORD64)(pUnwoundState->captureX19_X29[5]);
+ pRD->pCurrentContext->X25 = (DWORD64)(pUnwoundState->captureX19_X29[6]);
+ pRD->pCurrentContext->X26 = (DWORD64)(pUnwoundState->captureX19_X29[7]);
+ pRD->pCurrentContext->X27 = (DWORD64)(pUnwoundState->captureX19_X29[8]);
+ pRD->pCurrentContext->X28 = (DWORD64)(pUnwoundState->captureX19_X29[9]);
+ pRD->pCurrentContext->Fp = (DWORD64)(pUnwoundState->captureX19_X29[10]);
+ pRD->pCurrentContext->Lr = NULL; // Unwind again to get Caller's PC
+ 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;
+
+ pRD->pCurrentContext->X19 = *m_MachState.ptrX19_X29[0];
+ pRD->pCurrentContext->X20 = *m_MachState.ptrX19_X29[1];
+ pRD->pCurrentContext->X21 = *m_MachState.ptrX19_X29[2];
+ pRD->pCurrentContext->X22 = *m_MachState.ptrX19_X29[3];
+ pRD->pCurrentContext->X23 = *m_MachState.ptrX19_X29[4];
+ pRD->pCurrentContext->X24 = *m_MachState.ptrX19_X29[5];
+ pRD->pCurrentContext->X25 = *m_MachState.ptrX19_X29[6];
+ pRD->pCurrentContext->X26 = *m_MachState.ptrX19_X29[7];
+ pRD->pCurrentContext->X27 = *m_MachState.ptrX19_X29[8];
+ pRD->pCurrentContext->X28 = *m_MachState.ptrX19_X29[9];
+ pRD->pCurrentContext->Fp = *m_MachState.ptrX19_X29[10];
+ pRD->pCurrentContext->Lr = NULL; // Unwind again to get Caller's PC
+
+#if !defined(DACCESS_COMPILE)
+ pRD->pCurrentContextPointers->X19 = m_MachState.ptrX19_X29[0];
+ pRD->pCurrentContextPointers->X20 = m_MachState.ptrX19_X29[1];
+ pRD->pCurrentContextPointers->X21 = m_MachState.ptrX19_X29[2];
+ pRD->pCurrentContextPointers->X22 = m_MachState.ptrX19_X29[3];
+ pRD->pCurrentContextPointers->X23 = m_MachState.ptrX19_X29[4];
+ pRD->pCurrentContextPointers->X24 = m_MachState.ptrX19_X29[5];
+ pRD->pCurrentContextPointers->X25 = m_MachState.ptrX19_X29[6];
+ pRD->pCurrentContextPointers->X26 = m_MachState.ptrX19_X29[7];
+ pRD->pCurrentContextPointers->X27 = m_MachState.ptrX19_X29[8];
+ pRD->pCurrentContextPointers->X28 = m_MachState.ptrX19_X29[9];
+ pRD->pCurrentContextPointers->Fp = m_MachState.ptrX19_X29[10];
+ pRD->pCurrentContextPointers->Lr = NULL; // Unwind again to get Caller's PC
+#endif
+}
+#endif // CROSSGEN_COMPILE
+
+TADDR FixupPrecode::GetMethodDesc()
+{
+ LIMITED_METHOD_DAC_CONTRACT;
+
+ // This lookup is also manually inlined in PrecodeFixupThunk assembly code
+ TADDR base = *PTR_TADDR(GetBase());
+ if (base == NULL)
+ return NULL;
+ return base + (m_MethodDescChunkIndex * MethodDesc::ALIGNMENT);
+}
+
+#ifdef DACCESS_COMPILE
+void FixupPrecode::EnumMemoryRegions(CLRDataEnumMemoryFlags flags)
+{
+ SUPPORTS_DAC;
+ DacEnumMemoryRegion(dac_cast<TADDR>(this), sizeof(FixupPrecode));
+
+ DacEnumMemoryRegion(GetBase(), sizeof(TADDR));
+}
+#endif // DACCESS_COMPILE
+
+#ifndef DACCESS_COMPILE
+void StubPrecode::Init(MethodDesc* pMD, LoaderAllocator *pLoaderAllocator)
+{
+ WRAPPER_NO_CONTRACT;
+
+ int n = 0;
+
+ m_rgCode[n++] = 0x10000089; // adr x9, #16
+ m_rgCode[n++] = 0xA940312A; // ldp x10,x12,[x9]
+ m_rgCode[n++] = 0xD61F0140; // br x10
+
+ _ASSERTE(n+1 == _countof(m_rgCode));
+
+ m_pTarget = GetPreStubEntryPoint();
+ m_pMethodDesc = (TADDR)pMD;
+}
+
+#ifdef FEATURE_NATIVE_IMAGE_GENERATION
+void StubPrecode::Fixup(DataImage *image)
+{
+ WRAPPER_NO_CONTRACT;
+
+ image->FixupFieldToNode(this, offsetof(StubPrecode, m_pTarget),
+ image->GetHelperThunk(CORINFO_HELP_EE_PRESTUB),
+ 0,
+ IMAGE_REL_BASED_PTR);
+
+ image->FixupField(this, offsetof(StubPrecode, m_pMethodDesc),
+ (void*)GetMethodDesc(),
+ 0,
+ IMAGE_REL_BASED_PTR);
+}
+#endif // FEATURE_NATIVE_IMAGE_GENERATION
+
+void NDirectImportPrecode::Init(MethodDesc* pMD, LoaderAllocator *pLoaderAllocator)
+{
+ WRAPPER_NO_CONTRACT;
+
+ int n = 0;
+
+ m_rgCode[n++] = 0x1000008B; // adr x11, #16
+ m_rgCode[n++] = 0xA940316A; // ldp x10,x12,[x11]
+ m_rgCode[n++] = 0xD61F0140; // br x10
+
+ _ASSERTE(n+1 == _countof(m_rgCode));
+
+ m_pTarget = GetEEFuncEntryPoint(NDirectImportThunk);
+ m_pMethodDesc = (TADDR)pMD;
+}
+
+#ifdef FEATURE_NATIVE_IMAGE_GENERATION
+void NDirectImportPrecode::Fixup(DataImage *image)
+{
+ WRAPPER_NO_CONTRACT;
+
+ image->FixupField(this, offsetof(NDirectImportPrecode, m_pMethodDesc),
+ (void*)GetMethodDesc(),
+ 0,
+ IMAGE_REL_BASED_PTR);
+
+ image->FixupFieldToNode(this, offsetof(NDirectImportPrecode, m_pTarget),
+ image->GetHelperThunk(CORINFO_HELP_EE_PINVOKE_FIXUP),
+ 0,
+ IMAGE_REL_BASED_PTR);
+}
+#endif
+
+void FixupPrecode::Init(MethodDesc* pMD, LoaderAllocator *pLoaderAllocator, int iMethodDescChunkIndex /*=0*/, int iPrecodeChunkIndex /*=0*/)
+{
+ WRAPPER_NO_CONTRACT;
+
+ InitCommon();
+
+ // Initialize chunk indices only if they are not initialized yet. This is necessary to make MethodDesc::Reset work.
+ if (m_PrecodeChunkIndex == 0)
+ {
+ _ASSERTE(FitsInU1(iPrecodeChunkIndex));
+ m_PrecodeChunkIndex = static_cast<BYTE>(iPrecodeChunkIndex);
+ }
+
+ if (iMethodDescChunkIndex != -1)
+ {
+ if (m_MethodDescChunkIndex == 0)
+ {
+ _ASSERTE(FitsInU1(iMethodDescChunkIndex));
+ m_MethodDescChunkIndex = static_cast<BYTE>(iMethodDescChunkIndex);
+ }
+
+ if (*(void**)GetBase() == NULL)
+ *(void**)GetBase() = (BYTE*)pMD - (iMethodDescChunkIndex * MethodDesc::ALIGNMENT);
+ }
+
+ _ASSERTE(GetMethodDesc() == (TADDR)pMD);
+
+ if (pLoaderAllocator != NULL)
+ {
+ m_pTarget = GetEEFuncEntryPoint(PrecodeFixupThunk);
+ }
+}
+
+#ifdef FEATURE_NATIVE_IMAGE_GENERATION
+// Partial initialization. Used to save regrouped chunks.
+void FixupPrecode::InitForSave(int iPrecodeChunkIndex)
+{
+ STANDARD_VM_CONTRACT;
+
+ InitCommon();
+
+ _ASSERTE(FitsInU1(iPrecodeChunkIndex));
+ m_PrecodeChunkIndex = static_cast<BYTE>(iPrecodeChunkIndex);
+ // The rest is initialized in code:FixupPrecode::Fixup
+}
+
+void FixupPrecode::Fixup(DataImage *image, MethodDesc * pMD)
+{
+ STANDARD_VM_CONTRACT;
+
+ // Note that GetMethodDesc() does not return the correct value because of
+ // regrouping of MethodDescs into hot and cold blocks. That's why the caller
+ // has to supply the actual MethodDesc
+
+ SSIZE_T mdChunkOffset;
+ ZapNode * pMDChunkNode = image->GetNodeForStructure(pMD, &mdChunkOffset);
+ ZapNode * pHelperThunk = image->GetHelperThunk(CORINFO_HELP_EE_PRECODE_FIXUP);
+
+ image->FixupFieldToNode(this, offsetof(FixupPrecode, m_pTarget), pHelperThunk);
+
+ // Set the actual chunk index
+ FixupPrecode * pNewPrecode = (FixupPrecode *)image->GetImagePointer(this);
+
+ size_t mdOffset = mdChunkOffset - sizeof(MethodDescChunk);
+ size_t chunkIndex = mdOffset / MethodDesc::ALIGNMENT;
+ _ASSERTE(FitsInU1(chunkIndex));
+ pNewPrecode->m_MethodDescChunkIndex = (BYTE)chunkIndex;
+
+ // Fixup the base of MethodDescChunk
+ if (m_PrecodeChunkIndex == 0)
+ {
+ image->FixupFieldToNode(this, (BYTE *)GetBase() - (BYTE *)this,
+ pMDChunkNode, sizeof(MethodDescChunk));
+ }
+}
+#endif // FEATURE_NATIVE_IMAGE_GENERATION
+
+
+void ThisPtrRetBufPrecode::Init(MethodDesc* pMD, LoaderAllocator *pLoaderAllocator)
+{
+ WRAPPER_NO_CONTRACT;
+
+ int n = 0;
+ //Initially
+ //x0 -This ptr
+ //x1 -ReturnBuffer
+ m_rgCode[n++] = 0x91000010; // mov x16, x0
+ m_rgCode[n++] = 0x91000020; // mov x0, x1
+ m_rgCode[n++] = 0x91000201; // mov x1, x16
+ m_rgCode[n++] = 0x58000070; // ldr x16, [pc, #12]
+ _ASSERTE((UINT32*)&m_pTarget == &m_rgCode[n + 2]);
+ m_rgCode[n++] = 0xd61f0200; // br x16
+ n++; // empty 4 bytes for data alignment below
+ _ASSERTE(n == _countof(m_rgCode));
+
+
+ m_pTarget = GetPreStubEntryPoint();
+ m_pMethodDesc = (TADDR)pMD;
+}
+
+#ifndef CROSSGEN_COMPILE
+BOOL DoesSlotCallPrestub(PCODE pCode)
+{
+ PTR_DWORD pInstr = dac_cast<PTR_DWORD>(PCODEToPINSTR(pCode));
+
+ //FixupPrecode
+#if defined(HAS_FIXUP_PRECODE)
+ if (FixupPrecode::IsFixupPrecodeByASM(pCode))
+ {
+ PCODE pTarget = dac_cast<PTR_FixupPrecode>(pInstr)->m_pTarget;
+
+ if (isJump(pTarget))
+ {
+ pTarget = decodeJump(pTarget);
+ }
+
+ return pTarget == (TADDR)PrecodeFixupThunk;
+ }
+#endif
+
+ // StubPrecode
+ if (pInstr[0] == 0x10000089 && // adr x9, #16
+ pInstr[1] == 0xA940312A && // ldp x10,x12,[x9]
+ pInstr[2] == 0xD61F0140) // br x10
+ {
+ PCODE pTarget = dac_cast<PTR_StubPrecode>(pInstr)->m_pTarget;
+
+ if (isJump(pTarget))
+ {
+ pTarget = decodeJump(pTarget);
+ }
+
+ return pTarget == GetPreStubEntryPoint();
+ }
+
+ return FALSE;
+
+}
+
+#endif // CROSSGEN_COMPILE
+
+#endif // !DACCESS_COMPILE
+
+void UpdateRegDisplayFromCalleeSavedRegisters(REGDISPLAY * pRD, CalleeSavedRegisters * pCalleeSaved)
+{
+ LIMITED_METHOD_CONTRACT;
+
+ pRD->pCurrentContext->X19 = pCalleeSaved->x19;
+ pRD->pCurrentContext->X20 = pCalleeSaved->x20;
+ pRD->pCurrentContext->X21 = pCalleeSaved->x21;
+ pRD->pCurrentContext->X22 = pCalleeSaved->x22;
+ pRD->pCurrentContext->X23 = pCalleeSaved->x23;
+ pRD->pCurrentContext->X24 = pCalleeSaved->x24;
+ pRD->pCurrentContext->X25 = pCalleeSaved->x25;
+ pRD->pCurrentContext->X26 = pCalleeSaved->x26;
+ pRD->pCurrentContext->X27 = pCalleeSaved->x27;
+ pRD->pCurrentContext->X28 = pCalleeSaved->x28;
+ pRD->pCurrentContext->Fp = pCalleeSaved->x29;
+ pRD->pCurrentContext->Lr = pCalleeSaved->x30;
+
+ T_KNONVOLATILE_CONTEXT_POINTERS * pContextPointers = pRD->pCurrentContextPointers;
+ pContextPointers->X19 = (PDWORD64)&pCalleeSaved->x19;
+ pContextPointers->X20 = (PDWORD64)&pCalleeSaved->x20;
+ pContextPointers->X21 = (PDWORD64)&pCalleeSaved->x21;
+ pContextPointers->X22 = (PDWORD64)&pCalleeSaved->x22;
+ pContextPointers->X23 = (PDWORD64)&pCalleeSaved->x23;
+ pContextPointers->X24 = (PDWORD64)&pCalleeSaved->x24;
+ pContextPointers->X25 = (PDWORD64)&pCalleeSaved->x25;
+ pContextPointers->X26 = (PDWORD64)&pCalleeSaved->x26;
+ pContextPointers->X27 = (PDWORD64)&pCalleeSaved->x27;
+ pContextPointers->X28 = (PDWORD64)&pCalleeSaved->x28;
+ pContextPointers->Fp = (PDWORD64)&pCalleeSaved->x29;
+ pContextPointers->Lr = (PDWORD64)&pCalleeSaved->x30;
+}
+
+#ifndef CROSSGEN_COMPILE
+
+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 argumetn registers
+ ArgumentRegisters *pArgRegs = GetArgumentRegisters();
+ for (int i = 0; i < ARGUMENTREGISTERS_SIZE; i++)
+#ifdef __clang__
+ *(&pRD->pCurrentContext->X0 + (sizeof(void*)*i)) = pArgRegs->x[i];
+#else
+ pRD->pCurrentContext->X[i] = pArgRegs->x[i];
+#endif
+
+ // copy the callee saved regs
+ CalleeSavedRegisters *pCalleeSaved = GetCalleeSavedRegisters();
+ UpdateRegDisplayFromCalleeSavedRegisters(pRD, pCalleeSaved);
+
+ // copy the control registers
+ pRD->pCurrentContext->Fp = pCalleeSaved->x29;
+ pRD->pCurrentContext->Lr = pCalleeSaved->x30;
+ 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));
+
+
+}
+
+#endif
+
+#ifndef CROSSGEN_COMPILE
+
+void TailCallFrame::UpdateRegDisplay(const PREGDISPLAY pRD)
+{
+ _ASSERTE(!"ARM64:NYI");
+}
+
+#ifndef DACCESS_COMPILE
+void TailCallFrame::InitFromContext(T_CONTEXT * pContext)
+{
+ _ASSERTE(!"ARM64:NYI");
+}
+#endif // !DACCESS_COMPILE
+
+#endif // CROSSGEN_COMPILE
+
+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->X19 = (PDWORD64)&m_ctx.X19;
+ pRD->pCurrentContextPointers->X20 = (PDWORD64)&m_ctx.X20;
+ pRD->pCurrentContextPointers->X21 = (PDWORD64)&m_ctx.X21;
+ pRD->pCurrentContextPointers->X22 = (PDWORD64)&m_ctx.X22;
+ pRD->pCurrentContextPointers->X23 = (PDWORD64)&m_ctx.X23;
+ pRD->pCurrentContextPointers->X24 = (PDWORD64)&m_ctx.X24;
+ pRD->pCurrentContextPointers->X25 = (PDWORD64)&m_ctx.X25;
+ pRD->pCurrentContextPointers->X26 = (PDWORD64)&m_ctx.X26;
+ pRD->pCurrentContextPointers->X27 = (PDWORD64)&m_ctx.X27;
+ pRD->pCurrentContextPointers->X28 = (PDWORD64)&m_ctx.X28;
+ pRD->pCurrentContextPointers->Fp = (PDWORD64)&m_ctx.Fp;
+ pRD->pCurrentContextPointers->Lr = (PDWORD64)&m_ctx.Lr;
+
+ pRD->IsCallerContextValid = FALSE;
+ pRD->IsCallerSPValid = FALSE; // Don't add usage of this field. This is only temporary.
+}
+
+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;
+ }
+
+ // reset pContext; it's only valid for active (top-most) frame
+ pRD->pContext = NULL;
+
+ pRD->ControlPC = m_pCallerReturnAddress;
+ pRD->SP = (DWORD) dac_cast<TADDR>(m_pCallSiteSP);
+
+ pRD->IsCallerContextValid = FALSE;
+ pRD->IsCallerSPValid = FALSE;
+
+ pRD->pCurrentContext->Pc = m_pCallerReturnAddress;
+ pRD->pCurrentContext->Sp = pRD->SP;
+
+ // Update the frame pointer in the current context.
+ pRD->pCurrentContext->Fp = m_pCalleeSavedFP;
+ pRD->pCurrentContextPointers->Fp = &m_pCalleeSavedFP;
+
+ 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->X19 = &m_Regs->X19;
+ pRD->pCurrentContextPointers->X20 = &m_Regs->X20;
+ pRD->pCurrentContextPointers->X21 = &m_Regs->X21;
+ pRD->pCurrentContextPointers->X22 = &m_Regs->X22;
+ pRD->pCurrentContextPointers->X23 = &m_Regs->X23;
+ pRD->pCurrentContextPointers->X24 = &m_Regs->X24;
+ pRD->pCurrentContextPointers->X25 = &m_Regs->X25;
+ pRD->pCurrentContextPointers->X26 = &m_Regs->X26;
+ pRD->pCurrentContextPointers->X27 = &m_Regs->X27;
+ pRD->pCurrentContextPointers->X28 = &m_Regs->X28;
+ pRD->pCurrentContextPointers->Fp = &m_Regs->Fp;
+ pRD->pCurrentContextPointers->Lr = &m_Regs->Lr;
+
+ for (int i=0; i < 18; i++)
+ pRD->volatileCurrContextPointers.X[i] = &m_Regs->X[i];
+
+ pRD->IsCallerContextValid = FALSE;
+ pRD->IsCallerSPValid = FALSE; // Don't add usage of this field. This is only temporary.
+
+ 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->X0 = m_Args->X0;
+
+ pRD->pCurrentContext->X19 = m_Args->X19;
+ pRD->pCurrentContext->X20 = m_Args->X20;
+ pRD->pCurrentContext->X21 = m_Args->X21;
+ pRD->pCurrentContext->X22 = m_Args->X22;
+ pRD->pCurrentContext->X23 = m_Args->X23;
+ pRD->pCurrentContext->X24 = m_Args->X24;
+ pRD->pCurrentContext->X25 = m_Args->X25;
+ pRD->pCurrentContext->X26 = m_Args->X26;
+ pRD->pCurrentContext->X27 = m_Args->X27;
+ pRD->pCurrentContext->X28 = m_Args->X28;
+ pRD->pCurrentContext->Fp = m_Args->X29;
+ pRD->pCurrentContext->Lr = m_Args->Lr;
+
+ pRD->pCurrentContextPointers->X19 = &m_Args->X19;
+ pRD->pCurrentContextPointers->X20 = &m_Args->X20;
+ pRD->pCurrentContextPointers->X21 = &m_Args->X21;
+ pRD->pCurrentContextPointers->X22 = &m_Args->X22;
+ pRD->pCurrentContextPointers->X23 = &m_Args->X23;
+ pRD->pCurrentContextPointers->X24 = &m_Args->X24;
+ pRD->pCurrentContextPointers->X25 = &m_Args->X25;
+ pRD->pCurrentContextPointers->X26 = &m_Args->X26;
+ pRD->pCurrentContextPointers->X27 = &m_Args->X27;
+ pRD->pCurrentContextPointers->X28 = &m_Args->X28;
+ pRD->pCurrentContextPointers->Fp = &m_Args->X29;
+ pRD->pCurrentContextPointers->Lr = NULL;
+
+ SyncRegDisplayToCurrentContext(pRD);
+}
+#endif // FEATURE_HIJACK
+
+#ifdef FEATURE_COMINTEROP
+
+void emitCOMStubCall (ComCallMethodDesc *pCOMMethod, PCODE target)
+{
+ WRAPPER_NO_CONTRACT;
+
+ // adr x12, label_comCallMethodDesc
+ // ldr x10, label_target
+ // br x10
+ // 4 byte padding for alignment
+ // label_target:
+ // target address (8 bytes)
+ // label_comCallMethodDesc:
+ DWORD rgCode[] = {
+ 0x100000cc,
+ 0x5800006a,
+ 0xd61f0140
+ };
+
+ BYTE *pBuffer = (BYTE*)pCOMMethod - COMMETHOD_CALL_PRESTUB_SIZE;
+
+ memcpy(pBuffer, rgCode, sizeof(rgCode));
+ *((PCODE*)(pBuffer + sizeof(rgCode) + 4)) = target;
+
+ // Ensure that the updated instructions get actually written
+ ClrFlushInstructionCache(pBuffer, COMMETHOD_CALL_PRESTUB_SIZE);
+
+ _ASSERTE(IS_ALIGNED(pBuffer + COMMETHOD_CALL_PRESTUB_ADDRESS_OFFSET, sizeof(void*)) &&
+ *((PCODE*)(pBuffer + COMMETHOD_CALL_PRESTUB_ADDRESS_OFFSET)) == target);
+}
+#endif // FEATURE_COMINTEROP
+
+
+void JIT_ProfilerEnterLeaveTailcallStub(UINT_PTR ProfilerHandle)
+{
+ _ASSERTE(!"ARM64:NYI");
+}
+
+void JIT_TailCall()
+{
+ _ASSERTE(!"ARM64:NYI");
+}
+
+void InitJITHelpers1()
+{
+ return;
+}
+
+EXTERN_C void __stdcall ProfileEnterNaked(UINT_PTR clientData)
+{
+ _ASSERTE(!"ARM64:NYI");
+}
+EXTERN_C void __stdcall ProfileLeaveNaked(UINT_PTR clientData)
+{
+ _ASSERTE(!"ARM64:NYI");
+}
+EXTERN_C void __stdcall ProfileTailcallNaked(UINT_PTR clientData)
+{
+ _ASSERTE(!"ARM64:NYI");
+}
+
+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) && !defined (CROSSGEN_COMPILE)
+FaultingExceptionFrame *GetFrameFromRedirectedStubStackFrame (DISPATCHER_CONTEXT *pDispatcherContext)
+{
+ LIMITED_METHOD_CONTRACT;
+
+ return (FaultingExceptionFrame*)((TADDR)pDispatcherContext->ContextRecord->X19);
+}
+
+
+BOOL
+AdjustContextForVirtualStub(
+ EXCEPTION_RECORD *pExceptionRecord,
+ CONTEXT *pContext)
+{
+ LIMITED_METHOD_CONTRACT;
+
+ Thread * pThread = GetThread();
+
+ // 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(GetLR(pContext));
+
+ // Lr must already have been saved before calling so it should not be necessary to restore Lr
+
+ pExceptionRecord->ExceptionAddress = (PVOID)callsite;
+ SetIP(pContext, callsite);
+
+ return TRUE;
+}
+#endif // !(DACCESS_COMPILE && CROSSGEN_COMPILE)
+
+#ifdef FEATURE_COMINTEROP
+extern "C" void GenericComPlusCallStub(void)
+{
+ // This is not required for coreclr scenarios
+ throw "GenericComPlusCallStub is not implemented yet.";
+}
+#endif // FEATURE_COMINTEROP
+
+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] == 0x1000008c) &&
+ (pCode->m_code[1] == 0xa9403190) &&
+ (pCode->m_code[2] == 0xd61f0200))
+ {
+ return (UMEntryThunk*)pCode->m_pvSecretParam;
+ }
+
+ return NULL;
+}
+
+void UMEntryThunkCode::Encode(BYTE* pTargetCode, void* pvSecretParam)
+{
+ // adr x12, _label
+ // ldp x16, x12, [x12]
+ // br x16
+ // 4bytes padding
+ // _label
+ // m_pTargetCode data
+ // m_pvSecretParam data
+
+ m_code[0] = 0x1000008c;
+ m_code[1] = 0xa9403190;
+ m_code[2] = 0xd61f0200;
+
+
+ m_pTargetCode = (TADDR)pTargetCode;
+ m_pvSecretParam = (TADDR)pvSecretParam;
+
+ FlushInstructionCache(GetCurrentProcess(),&m_code,sizeof(m_code));
+}
+
+
+#ifdef PROFILING_SUPPORTED
+#include "proftoeeinterfaceimpl.h"
+
+extern UINT_PTR ProfileGetIPFromPlatformSpecificHandle(void * handle)
+{
+ _ASSERTE(!"ARM64:NYI");
+ return NULL;
+}
+
+extern void ProfileSetFunctionIDInPlatformSpecificHandle(void * pPlatformSpecificHandle, FunctionID functionID)
+{
+ _ASSERTE(!"ARM64:NYI");
+}
+
+ProfileArgIterator::ProfileArgIterator(MetaSig * pMetaSig, void* platformSpecificHandle)
+ : m_argIterator(pMetaSig)
+{
+ _ASSERTE(!"ARM64:NYI");
+}
+
+ProfileArgIterator::~ProfileArgIterator()
+{
+ _ASSERTE(!"ARM64:NYI");
+}
+
+LPVOID ProfileArgIterator::GetNextArgAddr()
+{
+ _ASSERTE(!"ARM64:NYI");
+ return NULL;
+}
+
+LPVOID ProfileArgIterator::GetHiddenArgValue(void)
+{
+ _ASSERTE(!"ARM64:NYI");
+ return NULL;
+}
+
+LPVOID ProfileArgIterator::GetThis(void)
+{
+ _ASSERTE(!"ARM64:NYI");
+ return NULL;
+}
+
+LPVOID ProfileArgIterator::GetReturnBufferAddr(void)
+{
+ _ASSERTE(!"ARM64:NYI");
+ return NULL;
+}
+#endif
+
+#if !defined(DACCESS_COMPILE)
+VOID ResetCurrentContext()
+{
+ LIMITED_METHOD_CONTRACT;
+}
+#endif
+
+LONG CLRNoCatchHandler(EXCEPTION_POINTERS* pExceptionInfo, PVOID pv)
+{
+ return EXCEPTION_CONTINUE_SEARCH;
+}
+
+void StompWriteBarrierEphemeral(bool isRuntimeSuspended)
+{
+ return;
+}
+
+void StompWriteBarrierResize(bool isRuntimeSuspended, bool bReqUpperBoundsCheck)
+{
+ return;
+}
+
+#ifdef DACCESS_COMPILE
+BOOL GetAnyThunkTarget (T_CONTEXT *pctx, TADDR *pTarget, TADDR *pTargetMethodDesc)
+{
+ _ASSERTE(!"ARM64:NYI");
+ return FALSE;
+}
+#endif // DACCESS_COMPILE
+
+#ifndef DACCESS_COMPILE
+// ----------------------------------------------------------------
+// StubLinkerCPU methods
+// ----------------------------------------------------------------
+
+void StubLinkerCPU::EmitMovConstant(IntReg target, UINT64 constant)
+{
+#define WORD_MASK 0xFFFF
+
+ // Move the 64bit constant in 4 chunks (of 16 bits).
+ // MOVZ Rd, <1st word>, LSL 0
+ // MOVK Rd, <2nd word>, LSL 1
+ // MOVK Rd, <3nd word>, LSL 2
+ // MOVK Rd, <4nd word>, LSL 3
+ WORD word = (WORD) (constant & WORD_MASK);
+ Emit32((DWORD)(0xD2<<24 | (4)<<21 | word<<5 | target));
+ if (!(constant & 0xFFFF)) return;
+
+ word = (WORD) ((constant>>16) & WORD_MASK);
+ if (word != 0)
+ Emit32((DWORD)(0xF2<<24 | (5)<<21 | word<<5 | target));
+ if (!(constant & 0xFFFFFFFF)) return;
+
+ word = (WORD) ((constant>>32) & WORD_MASK);
+ if (word != 0)
+ Emit32((DWORD)(0xF2<<24 | (6)<<21 | word<<5 | target));
+ if (!(constant & 0xFFFFFFFFFFFF)) return;
+
+ word = (WORD) ((constant>>48) & WORD_MASK);
+ if (word != 0)
+ Emit32((DWORD)(0xF2<<24 | (7)<<21 | word<<5 | target));
+#undef WORD_MASK
+}
+
+void StubLinkerCPU::EmitCmpImm(IntReg reg, int imm)
+{
+
+ if (0 <= imm && imm < 4096)
+ {
+ // CMP <Xn|SP>, #<imm>{, <shift>}
+ // Encoding: 1|1|1|1|0|0|0|0|shift(2)|imm(12)|Rn|Rt
+ // Where I encode shift as 0 and Rt has to be 1F
+ Emit32((DWORD) ((0xF1<<24) | ((0xFFF & imm)<<10) | (reg<<5) | (0x1F)) );
+
+ }
+ else
+ _ASSERTE(!"ARM64: NYI");
+}
+
+void StubLinkerCPU::EmitCmpReg(IntReg Xn, IntReg Xm)
+{
+
+ // Encoding for CMP (shifted register)
+ // sf|1|1|0|1|0|1|1|shift(2)|0|Xm(5)|imm(6)|Xn(5)|XZR(5)
+ // where
+ // sf = 1 for 64-bit variant,
+ // shift will be set to 00 (LSL)
+ // imm(6), which is the shift amount, will be set to 0
+
+ Emit32((DWORD) (0xEB<<24) | (Xm<<16) | (Xn<<5) | 0x1F);
+}
+
+void StubLinkerCPU::EmitCondFlagJump(CodeLabel * target, UINT cond)
+{
+ WRAPPER_NO_CONTRACT;
+ EmitLabelRef(target, reinterpret_cast<ConditionalBranchInstructionFormat&>(gConditionalBranchIF), cond);
+}
+
+void StubLinkerCPU::EmitJumpRegister(IntReg regTarget)
+{
+ // br regTarget
+ Emit32((DWORD) (0x3587C0<<10 | regTarget<<5));
+}
+
+void StubLinkerCPU::EmitProlog(unsigned short cIntRegArgs, unsigned short cVecRegArgs, unsigned short cCalleeSavedRegs, unsigned short cbStackSpace)
+{
+
+ _ASSERTE(!m_fProlog);
+
+ unsigned short numberOfEntriesOnStack = 2 + cIntRegArgs + cVecRegArgs + cCalleeSavedRegs; // 2 for fp, lr
+
+ // Stack needs to be 16 byte (2 qword) aligned. Compute the required padding before saving it
+ unsigned short totalPaddedFrameSize = static_cast<unsigned short>(ALIGN_UP(cbStackSpace + numberOfEntriesOnStack *sizeof(void*), 2*sizeof(void*)));
+ // The padding is going to be applied to the local stack
+ cbStackSpace = totalPaddedFrameSize - numberOfEntriesOnStack *sizeof(void*);
+
+ // Record the parameters of this prolog so that we can generate a matching epilog and unwind info.
+ DescribeProlog(cIntRegArgs, cVecRegArgs, cCalleeSavedRegs, cbStackSpace);
+
+
+
+ // N.B Despite the range of a jump with a sub sp is 4KB, we're limiting to 504 to save from emiting right prolog that's
+ // expressable in unwind codes efficiently. The largest offset in typical unwindinfo encodings that we use is 504.
+ // so allocations larger than 504 bytes would require setting the SP in multiple strides, which would complicate both
+ // prolog and epilog generation as well as unwindinfo generation.
+ _ASSERTE((totalPaddedFrameSize <= 504) && "NYI:ARM64 Implement StubLinker prologs with larger than 504 bytes of frame size");
+ if (totalPaddedFrameSize > 504)
+ COMPlusThrow(kNotSupportedException);
+
+ // Here is how the stack would look like (Stack grows up)
+ // [Low Address]
+ // +------------+
+ // SP -> | | <-+
+ // : : | Stack Frame, (i.e outgoing arguments) including padding
+ // | | <-+
+ // +------------+
+ // | FP |
+ // +------------+
+ // | LR |
+ // +------------+
+ // | X19 | <-+
+ // +------------+ |
+ // : : | Callee-saved registers
+ // +------------+ |
+ // | X28 | <-+
+ // +------------+
+ // | V0 | <-+
+ // +------------+ |
+ // : : | Vec Args
+ // +------------+ |
+ // | V7 | <-+
+ // +------------+
+ // | X0 | <-+
+ // +------------+ |
+ // : : | Int Args
+ // +------------+ |
+ // | X7 | <-+
+ // +------------+
+ // Old SP -> |[Stack Args]|
+ // [High Address]
+
+
+
+ // Regarding the order of operations in the prolog and epilog;
+ // If the prolog and the epilog matches each other we can simplify emitting the unwind codes and save a few
+ // bytes of unwind codes by making prolog and epilog share the same unwind codes.
+ // In order to do that we need to make the epilog be the reverse of the prolog.
+ // But we wouldn't want to add restoring of the argument registers as that's completely unnecessary.
+ // Besides, saving argument registers cannot be expressed by the unwind code encodings.
+ // So, we'll push saving the argument registers to the very last in the prolog, skip restoring it in epilog,
+ // and also skip reporting it to the OS.
+ //
+ // Another bit that we can save is resetting the frame pointer.
+ // This is not necessary when the SP doesn't get modified beyond prolog and epilog. (i.e no alloca/localloc)
+ // And in that case we don't need to report setting up the FP either.
+
+
+
+ // 1. Relocate SP
+ EmitSubImm(RegSp, RegSp, totalPaddedFrameSize);
+
+ unsigned cbOffset = 2*sizeof(void*) + cbStackSpace; // 2 is for fp,lr
+
+ // 2. Store callee-saved registers
+ _ASSERTE(cCalleeSavedRegs <= 10);
+ for (unsigned short i=0; i<(cCalleeSavedRegs/2)*2; i+=2)
+ EmitLoadStoreRegPairImm(eSTORE, IntReg(19+i), IntReg(19+i+1), RegSp, cbOffset + i*sizeof(void*));
+ if ((cCalleeSavedRegs %2) ==1)
+ EmitLoadStoreRegImm(eSTORE, IntReg(cCalleeSavedRegs-1), RegSp, cbOffset + (cCalleeSavedRegs-1)*sizeof(void*));
+
+ // 3. Store FP/LR
+ EmitLoadStoreRegPairImm(eSTORE, RegFp, RegLr, RegSp, cbStackSpace);
+
+ // 4. Set the frame pointer
+ EmitMovReg(RegFp, RegSp);
+
+ // 5. Store floating point argument registers
+ cbOffset += cCalleeSavedRegs*sizeof(void*);
+ _ASSERTE(cVecRegArgs <= 8);
+ for (unsigned short i=0; i<(cVecRegArgs/2)*2; i+=2)
+ EmitLoadStoreRegPairImm(eSTORE, VecReg(i), VecReg(i+1), RegSp, cbOffset + i*sizeof(void*));
+ if ((cVecRegArgs % 2) == 1)
+ EmitLoadStoreRegImm(eSTORE, VecReg(cVecRegArgs-1), RegSp, cbOffset + (cVecRegArgs-1)*sizeof(void*));
+
+ // 6. Store int argument registers
+ cbOffset += cVecRegArgs*sizeof(void*);
+ _ASSERTE(cIntRegArgs <= 8);
+ for (unsigned short i=0 ; i<(cIntRegArgs/2)*2; i+=2)
+ EmitLoadStoreRegPairImm(eSTORE, IntReg(i), IntReg(i+1), RegSp, cbOffset + i*sizeof(void*));
+ if ((cIntRegArgs % 2) == 1)
+ EmitLoadStoreRegImm(eSTORE,IntReg(cIntRegArgs-1), RegSp, cbOffset + (cIntRegArgs-1)*sizeof(void*));
+}
+
+void StubLinkerCPU::EmitEpilog()
+{
+ _ASSERTE(m_fProlog);
+
+ // 6. Restore int argument registers
+ // nop: We don't need to. They are scratch registers
+
+ // 5. Restore floating point argument registers
+ // nop: We don't need to. They are scratch registers
+
+ // 4. Restore the SP from FP
+ // N.B. We're assuming that the stublinker stubs doesn't do alloca, hence nop
+
+ // 3. Restore FP/LR
+ EmitLoadStoreRegPairImm(eLOAD, RegFp, RegLr, RegSp, m_cbStackSpace);
+
+ // 2. restore the calleeSavedRegisters
+ unsigned cbOffset = 2*sizeof(void*) + m_cbStackSpace; // 2 is for fp,lr
+ if ((m_cCalleeSavedRegs %2) ==1)
+ EmitLoadStoreRegImm(eLOAD, IntReg(m_cCalleeSavedRegs-1), RegSp, cbOffset + (m_cCalleeSavedRegs-1)*sizeof(void*));
+ for (int i=(m_cCalleeSavedRegs/2)*2-2; i>=0; i-=2)
+ EmitLoadStoreRegPairImm(eLOAD, IntReg(19+i), IntReg(19+i+1), RegSp, cbOffset + i*sizeof(void*));
+
+ // 1. Restore SP
+ EmitAddImm(RegSp, RegSp, GetStackFrameSize());
+ EmitRet(RegLr);
+}
+
+void StubLinkerCPU::EmitRet(IntReg Xn)
+{
+ // Encoding: 1101011001011111000000| Rn |00000
+ Emit32((DWORD)(0xD65F0000 | (Xn << 5)));
+}
+
+void StubLinkerCPU::EmitLoadStoreRegPairImm(DWORD flags, IntReg Xt1, IntReg Xt2, IntReg Xn, int offset)
+{
+ EmitLoadStoreRegPairImm(flags, (int)Xt1, (int)Xt2, Xn, offset, FALSE);
+}
+
+void StubLinkerCPU::EmitLoadStoreRegPairImm(DWORD flags, VecReg Vt1, VecReg Vt2, IntReg Xn, int offset)
+{
+ EmitLoadStoreRegPairImm(flags, (int)Vt1, (int)Vt2, Xn, offset, TRUE);
+}
+
+void StubLinkerCPU::EmitLoadStoreRegPairImm(DWORD flags, int regNum1, int regNum2, IntReg Xn, int offset, BOOL isVec)
+{
+ // Encoding:
+ // [opc(2)] | 1 | 0 | 1 | [IsVec(1)] | 0 | [!postIndex(1)] | [writeBack(1)] | [isLoad(1)] | [imm(7)] | [Xt2(5)] | [Xn(5)] | [Xt1(5)]
+ // where opc=01 and if isVec==1, opc=10 otherwise
+
+ BOOL isLoad = flags & 1;
+ BOOL writeBack = flags & 2;
+ BOOL postIndex = flags & 4;
+ _ASSERTE((-512 <= offset) && (offset <= 504));
+ _ASSERTE((offset & 7) == 0);
+ int opc = isVec ? 1 : 2;
+ Emit32((DWORD) ( (opc<<30) | // opc
+ (0x5<<27) |
+ (!!isVec<<26) |
+ (!postIndex<<24) |
+ (!!writeBack<<23) |
+ (!!isLoad<<22) |
+ ((0x7F & (offset >> 3)) << 15) |
+ (regNum2 << 10) |
+ (Xn << 5) |
+ (regNum1)
+ ));
+
+}
+
+
+void StubLinkerCPU::EmitLoadStoreRegImm(DWORD flags, IntReg Xt, IntReg Xn, int offset)
+{
+ EmitLoadStoreRegImm(flags, (int)Xt, Xn, offset, FALSE);
+}
+void StubLinkerCPU::EmitLoadStoreRegImm(DWORD flags, VecReg Vt, IntReg Xn, int offset)
+{
+ EmitLoadStoreRegImm(flags, (int)Vt, Xn, offset, TRUE);
+}
+
+void StubLinkerCPU::EmitLoadStoreRegImm(DWORD flags, int regNum, IntReg Xn, int offset, BOOL isVec)
+{
+ // Encoding:
+ // wb=1 : [size(2)=11] | 1 | 1 | 1 | [IsVec(1)] | 0 | [!writeBack(1)] | 0 | [isLoad(1)] | 0 | [imm(7)] | [!postIndex(1)] | [Xn(5)] | [Xt(5)]
+ // wb=0 : [size(2)=11] | 1 | 1 | 1 | [IsVec(1)] | 0 | [!writeBack(1)] | 0 | [isLoad(1)] | [ imm(12) ] | [Xn(5)] | [Xt(5)]
+ // where IsVec=0 for IntReg, 1 for VecReg
+
+ BOOL isLoad = flags & 1;
+ BOOL writeBack = flags & 2;
+ BOOL postIndex = flags & 4;
+ if (writeBack)
+ {
+ _ASSERTE(-256 <= offset && offset <= 255);
+ Emit32((DWORD) ( (0x1F<<27) |
+ (!!isVec<<26) |
+ (!writeBack<<24) |
+ (!!isLoad<<22) |
+ ((0x1FF & offset) << 12) |
+ (!postIndex<<11) |
+ (0x1<<10) |
+ (Xn<<5) |
+ (regNum))
+ );
+ }
+ else
+ {
+ _ASSERTE((0 <= offset) && (offset <= 32760));
+ _ASSERTE((offset & 7) == 0);
+ Emit32((DWORD) ( (0x1F<<27) |
+ (!!isVec<<26) |
+ (!writeBack<<24) |
+ (!!isLoad<<22) |
+ ((0xFFF & (offset >> 3)) << 10) |
+ (Xn<<5) |
+ (regNum))
+ );
+ }
+
+
+
+}
+
+// Load Register (Register Offset)
+void StubLinkerCPU::EmitLoadRegReg(IntReg Xt, IntReg Xn, IntReg Xm, DWORD option)
+{
+ Emit32((DWORD) ( (0xF8600800) |
+ (option << 12) |
+ (Xm << 16) |
+ (Xn << 5) |
+ (Xt)
+ ));
+
+}
+
+void StubLinkerCPU::EmitMovReg(IntReg Xd, IntReg Xm)
+{
+ if (Xd == RegSp || Xm == RegSp)
+ {
+ // This is a different encoding than the regular MOV (register) below.
+ // Note that RegSp and RegZero share the same encoding.
+ // TODO: check that the intention is not mov Xd, XZR
+ // MOV <Xd|SP>, <Xn|SP>
+ // which is equivalent to
+ // ADD <Xd|SP>, <Xn|SP>, #0
+ // Encoding: sf|0|0|1|0|0|0|1|shift(2)|imm(12)|Xn|Xd
+ // where
+ // sf = 1 -> 64-bit variant
+ // shift and imm12 are both 0
+ Emit32((DWORD) (0x91000000 | (Xm << 5) | Xd));
+ }
+ else
+ {
+ // MOV <Xd>, <Xm>
+ // which is eqivalent to
+ // ORR <Xd>. XZR, <Xm>
+ // Encoding: sf|0|1|0|1|0|1|0|shift(2)|0|Xm|imm(6)|Xn|Xd
+ // where
+ // sf = 1 -> 64-bit variant
+ // shift and imm6 are both 0
+ // Xn = XZR
+ Emit32((DWORD) ( (0xAA << 24) | (Xm << 16) | (0x1F << 5) | Xd));
+ }
+}
+
+void StubLinkerCPU::EmitSubImm(IntReg Xd, IntReg Xn, unsigned int value)
+{
+ // sub <Xd|SP>, <Xn|SP>, #imm{, <shift>}
+ // Encoding: sf|1|0|1|0|0|0|1|shift(2)|imm(12)|Rn|Rd
+ // where <shift> is encoded as LSL #0 (no shift) when shift=00 and LSL #12 when shift=01. (No shift in this impl)
+ // imm(12) is an unsigned immediate in the range of 0 to 4095
+ // Rn and Rd are both encoded as SP=31
+ // sf = 1 for 64-bit variant
+ _ASSERTE((0 <= value) && (value <= 4095));
+ Emit32((DWORD) ((0xD1 << 24) | (value << 10) | (Xd << 5) | Xn));
+
+}
+
+void StubLinkerCPU::EmitAddImm(IntReg Xd, IntReg Xn, unsigned int value)
+{
+ // add SP, SP, #imm{, <shift>}
+ // Encoding: sf|0|0|1|0|0|0|1|shift(2)|imm(12)|Rn|Rd
+ // where <shift> is encoded as LSL #0 (no shift) when shift=00 and LSL #12 when shift=01. (No shift in this impl)
+ // imm(12) is an unsigned immediate in the range of 0 to 4095
+ // Rn and Rd are both encoded as SP=31
+ // sf = 1 for 64-bit variant
+ _ASSERTE((0 <= value) && (value <= 4095));
+ Emit32((DWORD) ((0x91 << 24) | (value << 10) | (Xn << 5) | Xd));
+}
+
+void StubLinkerCPU::EmitCallRegister(IntReg reg)
+{
+ // blr Xn
+ // Encoding: 1|1|0|1|0|1|1|0|0|0|1|1|1|1|1|1|0|0|0|0|0|Rn|0|0|0|0|0
+ Emit32((DWORD) (0xD63F0000 | (reg << 5)));
+}
+
+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 x0 holds the delegate instance. Look up the real target address stored in the MethodPtrAux
+ // field and save it in x9. Tailcall to the target method after re-arranging the arguments
+ // ldr x9, [x0, #offsetof(DelegateObject, _methodPtrAux)]
+ EmitLoadStoreRegImm(eLOAD, IntReg(9), IntReg(0), DelegateObject::GetOffsetOfMethodPtrAux());
+ //add x11, x0, DelegateObject::GetOffsetOfMethodPtrAux() - load the indirection cell into x11 used by ResolveWorkerAsmStub
+ EmitAddImm(IntReg(11), IntReg(0), DelegateObject::GetOffsetOfMethodPtrAux());
+
+ for (ShuffleEntry* pEntry = pShuffleEntryArray; pEntry->srcofs != ShuffleEntry::SENTINEL; pEntry++)
+ {
+ if (pEntry->srcofs & ShuffleEntry::REGMASK)
+ {
+ // If source is present in register then destination must also be a register
+ _ASSERTE(pEntry->dstofs & ShuffleEntry::REGMASK);
+
+ EmitMovReg(IntReg(pEntry->dstofs & ShuffleEntry::OFSMASK), IntReg(pEntry->srcofs & ShuffleEntry::OFSMASK));
+ }
+ else if (pEntry->dstofs & ShuffleEntry::REGMASK)
+ {
+ // source must be on the stack
+ _ASSERTE(!(pEntry->srcofs & ShuffleEntry::REGMASK));
+
+ EmitLoadStoreRegImm(eLOAD, IntReg(pEntry->dstofs & ShuffleEntry::OFSMASK), 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(8), RegSp, pEntry->srcofs * sizeof(void*));
+ EmitLoadStoreRegImm(eSTORE, IntReg(8), RegSp, pEntry->dstofs * sizeof(void*));
+ }
+ }
+
+ // Tailcall to target
+ // br x9
+ EmitJumpRegister(IntReg(9));
+}
+
+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);
+ }
+}
+
+#ifndef CROSSGEN_COMPILE
+
+EXTERN_C UINT32 _tls_index;
+void StubLinkerCPU::EmitGetThreadInlined(IntReg Xt)
+{
+#if defined(FEATURE_IMPLICIT_TLS) && !defined(FEATURE_PAL)
+ // Trashes x8.
+ IntReg X8 = IntReg(8);
+ _ASSERTE(Xt != X8);
+
+ // Load the _tls_index
+ EmitLabelRef(NewExternalCodeLabel((LPVOID)&_tls_index), reinterpret_cast<LoadFromLabelInstructionFormat&>(gLoadFromLabelIF), X8);
+
+ // Load Teb->ThreadLocalStoragePointer into x8
+ EmitLoadStoreRegImm(eLOAD, Xt, IntReg(18), offsetof(_TEB, ThreadLocalStoragePointer));
+
+ // index it with _tls_index, i.e Teb->ThreadLocalStoragePointer[_tls_index].
+ // This will give us the TLS section for the module on this thread's context
+ EmitLoadRegReg(Xt, Xt, X8, eLSL);
+
+ // read the Thread* from TLS section
+ EmitAddImm(Xt, Xt, OFFSETOF__TLS__tls_CurrentThread);
+ EmitLoadStoreRegImm(eLOAD, Xt, Xt, 0);
+#else
+ _ASSERTE(!"NYI:StubLinkerCPU::EmitGetThreadInlined");
+#endif
+
+}
+
+void StubLinkerCPU::EmitUnboxMethodStub(MethodDesc *pMD)
+{
+ _ASSERTE(!pMD->RequiresInstMethodDescArg());
+
+ // Address of the value type is address of the boxed instance plus sizeof(MethodDesc*).
+ // add x0, #sizeof(MethodDesc*)
+ EmitAddImm(IntReg(0), IntReg(0), sizeof(MethodDesc*));
+
+ // Tail call the real target.
+ EmitCallManagedMethod(pMD, TRUE /* tail call */);
+}
+
+#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 * pStart = (BYTE *)(void *)pAllocator->GetDynamicHelpersHeap()->AllocAlignedMem(cbAligned, DYNAMIC_HELPER_ALIGNMENT); \
+ BYTE * p = pStart;
+
+#define END_DYNAMIC_HELPER_EMIT() \
+ _ASSERTE(pStart + cb == p); \
+ while (p < pStart + cbAligned) { *(DWORD*)p = 0xBADC0DF0; p += 4; }\
+ ClrFlushInstructionCache(pStart, cbAligned); \
+ return (PCODE)pStart
+
+// Uses x8 as scratch register to store address of data label
+// After load x8 is increment to point to next data
+// only accepts positive offsets
+static void LoadRegPair(BYTE* p, int reg1, int reg2, UINT32 offset)
+{
+ LIMITED_METHOD_CONTRACT;
+
+ // adr x8, <label>
+ *(DWORD*)(p + 0) = 0x10000008 | ((offset >> 2) << 5);
+ // ldp reg1, reg2, [x8], #16 ; postindex & wback
+ *(DWORD*)(p + 4) = 0xa8c10100 | (reg2 << 10) | reg1;
+}
+
+PCODE DynamicHelpers::CreateHelper(LoaderAllocator * pAllocator, TADDR arg, PCODE target)
+{
+ STANDARD_VM_CONTRACT;
+
+ BEGIN_DYNAMIC_HELPER_EMIT(32);
+
+ // adr x8, <label>
+ // ldp x0, x12, [x8]
+ LoadRegPair(p, 0, 12, 16);
+ p += 8;
+ // br x12
+ *(DWORD*)p = 0xd61f0180;
+ p += 4;
+
+ // padding to make 8 byte aligned
+ *(DWORD*)p = 0xBADC0DF0; 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, LoaderAllocator * pAllocator, TADDR arg, PCODE target)
+{
+ STANDARD_VM_CONTRACT;
+
+ // if p is already aligned at 8-byte then padding is required for data alignment
+ bool padding = (((uintptr_t)p & 0x7) == 0);
+
+ // adr x8, <label>
+ // ldp x1, x12, [x8]
+ LoadRegPair(p, 1, 12, padding?16:12);
+ p += 8;
+
+ // br x12
+ *(DWORD*)p = 0xd61f0180;
+ p += 4;
+
+ if(padding)
+ {
+ // padding to make 8 byte aligned
+ *(DWORD*)p = 0xBADC0DF0;
+ p += 4;
+ }
+
+ // 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, 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(40);
+
+ // adr x8, <label>
+ // ldp x0, x1, [x8] ; wback
+ LoadRegPair(p, 0, 1, 16);
+ p += 8;
+
+ // ldr x12, [x8]
+ *(DWORD*)p = 0xf940010c;
+ p += 4;
+ // br x12
+ *(DWORD*)p = 0xd61f0180;
+ 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(32);
+
+ // mov x1, x0
+ *(DWORD*)p = 0x91000001;
+ p += 4;
+
+ // adr x8, <label>
+ // ldp x0, x12, [x8]
+ LoadRegPair(p, 0, 12, 12);
+ p += 8;
+
+ // br x12
+ *(DWORD*)p = 0xd61f0180;
+ 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);
+
+ // br lr
+ *(DWORD*)p = 0xd61f03c0;
+ p += 4;
+ END_DYNAMIC_HELPER_EMIT();
+}
+
+PCODE DynamicHelpers::CreateReturnConst(LoaderAllocator * pAllocator, TADDR arg)
+{
+ STANDARD_VM_CONTRACT;
+
+ BEGIN_DYNAMIC_HELPER_EMIT(16);
+
+ // ldr x0, <lable>
+ *(DWORD*)p = 0x58000040;
+ p += 4;
+
+ // br lr
+ *(DWORD*)p = 0xd61f03c0;
+ 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(24);
+
+ // ldr x0, <label>
+ *(DWORD*)p = 0x58000080;
+ p += 4;
+
+ // ldr x0, [x0]
+ *(DWORD*)p = 0xf9400000;
+ p += 4;
+
+ // add x0, x0, offset
+ *(DWORD*)p = 0x91000000 | (offset << 10);
+ p += 4;
+
+ // br lr
+ *(DWORD*)p = 0xd61f03c0;
+ 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);
+
+ // adr x8, <label>
+ // ldp x2, x12, [x8]
+ LoadRegPair(p, 2, 12, 16);
+ p += 8;
+
+ // br x12
+ *(DWORD*)p = 0xd61f0180;
+ p += 4;
+
+ // padding to make 8 byte aligned
+ *(DWORD*)p = 0xBADC0DF0; 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(40);
+
+ // adr x8, <label>
+ // ldp x2, x3, [x8]; wback
+ LoadRegPair(p, 2, 3, 16);
+ p += 8;
+
+ // ldr x12, [x8]
+ *(DWORD*)p = 0xf940010c;
+ p += 4;
+
+ // br x12
+ *(DWORD*)p = 0xd61f0180;
+ 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);
+ pArgs->dictionaryIndexAndSlot = dictionaryIndexAndSlot;
+ pArgs->signature = pLookup->signature;
+ pArgs->module = (CORINFO_MODULE_HANDLE)pModule;
+
+ // It's available only via the run-time helper function
+ if (pLookup->indirections == CORINFO_USEHELPER)
+ {
+ BEGIN_DYNAMIC_HELPER_EMIT(32);
+
+ // X0 already contains generic context parameter
+ // reuse EmitHelperWithArg for below two operations
+ // X1 <- pArgs
+ // branch to helperAddress
+ EmitHelperWithArg(p, pAllocator, (TADDR)pArgs, helperAddress);
+
+ END_DYNAMIC_HELPER_EMIT();
+ }
+ else
+ {
+ int indirectionsCodeSize = 0;
+ int indirectionsDataSize = 0;
+ for (WORD i = 0; i < pLookup->indirections; i++) {
+ indirectionsCodeSize += (pLookup->offsets[i] > 32760 ? 8 : 4); // if( > 32760) (8 code bytes) else 4 bytes for instruction with offset encoded in instruction
+ indirectionsDataSize += (pLookup->offsets[i] > 32760 ? 4 : 0); // 4 bytes for storing indirection offset values
+ }
+
+ int codeSize = indirectionsCodeSize;
+ if(pLookup->testForNull)
+ {
+ codeSize += 4; // mov
+ codeSize += 12; // cbz-ret-mov
+ //padding for 8-byte align (required by EmitHelperWithArg)
+ if((codeSize & 0x7) == 0)
+ codeSize += 4;
+ codeSize += 28; // size of EmitHelperWithArg
+ }
+ else
+ {
+ codeSize += 4 ; /* ret */
+ }
+
+ codeSize += indirectionsDataSize;
+
+ BEGIN_DYNAMIC_HELPER_EMIT(codeSize);
+
+ if (pLookup->testForNull)
+ {
+ // mov x9, x0
+ *(DWORD*)p = 0x91000009;
+ p += 4;
+ }
+
+ // moving offset value wrt PC. Currently points to first indirection offset data.
+ uint dataOffset = codeSize - indirectionsDataSize - (pLookup->testForNull ? 4 : 0);
+ for (WORD i = 0; i < pLookup->indirections; i++)
+ {
+ if(pLookup->offsets[i] > 32760)
+ {
+ // ldr w10, [PC, #dataOffset]
+ *(DWORD*)p = 0x1800000a | ((dataOffset>>2)<<5);
+ p += 4;
+ // ldr x0, [x0, x10]
+ *(DWORD*)p = 0xf86a6800;
+ p += 4;
+
+ // move to next indirection offset data
+ dataOffset = dataOffset - 8 + 4; // subtract 8 as we have moved PC by 8 and add 4 as next data is at 4 bytes from previous data
+ }
+ else
+ {
+ // offset must be 8 byte aligned
+ _ASSERTE((pLookup->offsets[i] & 0x7) == 0);
+
+ // ldr x0, [x0, #(pLookup->offsets[i])]
+ *(DWORD*)p = 0xf9400000 | ( ((UINT32)pLookup->offsets[i]>>3) <<10 );
+ p += 4;
+ dataOffset -= 4; // subtract 4 as we have moved PC by 4
+ }
+ }
+
+ // No null test required
+ if (!pLookup->testForNull)
+ {
+ // ret lr
+ *(DWORD*)p = 0xd65f03c0;
+ p += 4;
+ }
+ else
+ {
+ // cbz x0, nullvaluelabel
+ *(DWORD*)p = 0xb4000040;
+ p += 4;
+ // ret lr
+ *(DWORD*)p = 0xd65f03c0;
+ p += 4;
+ // nullvaluelabel:
+ // mov x0, x9
+ *(DWORD*)p = 0x91000120;
+ p += 4;
+ // reuse EmitHelperWithArg for below two operations
+ // X1 <- pArgs
+ // branch to helperAddress
+ EmitHelperWithArg(p, pAllocator, (TADDR)pArgs, helperAddress);
+ }
+
+ // datalabel:
+ for (WORD i = 0; i < pLookup->indirections; i++)
+ {
+ if(pLookup->offsets[i] > 32760)
+ {
+ _ASSERTE((pLookup->offsets[i] & 0xffffffff00000000) == 0);
+ *(UINT32*)p = (UINT32)pLookup->offsets[i];
+ p += 4;
+ }
+ }
+
+ END_DYNAMIC_HELPER_EMIT();
+ }
+}
+#endif // FEATURE_READYTORUN
+
+#endif // CROSSGEN_COMPILE
+
+#endif // #ifndef DACCESS_COMPILE