summaryrefslogtreecommitdiff
path: root/src/vm/arm64/asmhelpers.S
diff options
context:
space:
mode:
Diffstat (limited to 'src/vm/arm64/asmhelpers.S')
-rw-r--r--src/vm/arm64/asmhelpers.S836
1 files changed, 836 insertions, 0 deletions
diff --git a/src/vm/arm64/asmhelpers.S b/src/vm/arm64/asmhelpers.S
new file mode 100644
index 0000000000..e3011ce7b0
--- /dev/null
+++ b/src/vm/arm64/asmhelpers.S
@@ -0,0 +1,836 @@
+// 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.
+
+#include "asmconstants.h"
+#include "unixasmmacros.inc"
+
+// LPVOID __stdcall GetCurrentIP(void)//
+LEAF_ENTRY GetCurrentIP, _TEXT
+ mov x0, lr
+ ret lr
+LEAF_END GetCurrentIP, _TEXT
+
+// LPVOID __stdcall GetCurrentSP(void)//
+LEAF_ENTRY GetCurrentSP, _TEXT
+ mov x0, sp
+ ret lr
+LEAF_END GetCurrentSP, _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
+ mov w1, #0
+ str w1, [x0, #MachState__isValid]
+
+ str lr, [x0, #LazyMachState_captureIp]
+
+ // str instruction does not save sp register directly so move to temp register
+ mov x1, sp
+ str x1, [x0, #LazyMachState_captureSp]
+
+ // save non-volatile registers that can contain object references
+ add x1, x0, #LazyMachState_captureX19_X29
+ stp x19, x20, [x1, #(16*0)]
+ stp x21, x22, [x1, #(16*1)]
+ stp x23, x24, [x1, #(16*2)]
+ stp x25, x26, [x1, #(16*3)]
+ stp x27, x28, [x1, #(16*4)]
+ str x29, [x1, #(16*5)]
+
+ ret lr
+LEAF_END LazyMachStateCaptureState, _TEXT
+
+//
+// If a preserved register were pushed onto the stack between
+// the managed caller and the H_M_F, ptrX19_X29 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 ptrX19_X29 points into the MachState, we need to update
+// the register here. That's what this macro does.
+//
+.macro RestoreRegMS regIndex, reg
+ // Incoming:
+ //
+ // x0 = address of MachState
+ //
+ // $regIndex: Index of the register (x19-x28). For x19, index is 19.
+ //For x20, index is 20, and so on.
+ //
+ // $reg: Register name (e.g. x19, x20, etc)
+ //
+ // Get the address of the specified captured register from machine state
+ add x2, x0, #(MachState__captureX19_X29 + ((\regIndex-19)*8))
+
+ // Get the content of specified preserved register pointer from machine state
+ ldr x3, [x0, #(MachState__ptrX19_X29 + ((\regIndex-19)*8))]
+
+ cmp x2, x3
+ bne LOCAL_LABEL(NoRestore_\reg)
+ ldr \reg, [x2]
+LOCAL_LABEL(NoRestore_\reg):
+
+.endmacro
+
+// EXTERN_C int __fastcall HelperMethodFrameRestoreState(
+// INDEBUG_COMMA(HelperMethodFrame *pFrame)
+// MachState *pState
+// )
+LEAF_ENTRY HelperMethodFrameRestoreState, _TEXT
+
+ #ifdef _DEBUG
+ mov x0, x1
+ #endif
+
+ // If machine state is invalid, then simply exit
+ ldr x1, [x0, #MachState__isValid]
+ cmp x1, #0
+ beq LOCAL_LABEL(Done)
+
+ RestoreRegMS 19, X19
+ RestoreRegMS 20, X20
+ RestoreRegMS 21, X21
+ RestoreRegMS 22, X22
+ RestoreRegMS 23, X23
+ RestoreRegMS 24, X24
+ RestoreRegMS 25, X25
+ RestoreRegMS 26, X26
+ RestoreRegMS 27, X27
+ RestoreRegMS 28, X28
+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.
+ mov x0,#0
+ ret lr
+
+LEAF_END HelperMethodFrameRestoreState, _TEXT
+
+// ------------------------------------------------------------------
+// The call in ndirect import precode points to this function.
+NESTED_ENTRY NDirectImportThunk, _TEXT, NoHandler
+
+ PROLOG_SAVE_REG_PAIR_INDEXED fp, lr, #-144
+ SAVE_ARGUMENT_REGISTERS sp, 16
+ SAVE_FLOAT_ARGUMENT_REGISTERS sp, 80
+
+ mov x0, x12
+ bl NDirectImportWorker
+ mov x12, x0
+
+ // pop the stack and restore original register state
+ RESTORE_FLOAT_ARGUMENT_REGISTERS sp, 80
+ RESTORE_ARGUMENT_REGISTERS sp, 16
+ EPILOG_RESTORE_REG_PAIR_INDEXED fp, lr, #144
+
+ // If we got back from NDirectImportWorker, the MD has been successfully
+ // linked. Proceed to execute the original DLL call.
+ EPILOG_BRANCH_REG x12
+
+NESTED_END NDirectImportThunk, _TEXT
+
+// ------------------------------------------------------------------
+// ARM64TODO: Implement PrecodeFixupThunk when PreCode is Enabled
+NESTED_ENTRY PrecodeFixupThunk, _TEXT, NoHandler
+ brk #0
+NESTED_END PrecodeFixupThunk, _TEXT
+// ------------------------------------------------------------------
+
+NESTED_ENTRY ThePreStub, _TEXT, NoHandler
+
+ PROLOG_WITH_TRANSITION_BLOCK
+
+ add x0, sp, #__PWTB_TransitionBlock // pTransitionBlock
+ mov x1, METHODDESC_REGISTER // pMethodDesc
+
+ bl PreStubWorker
+
+ mov x9, x0
+
+ EPILOG_WITH_TRANSITION_BLOCK_TAILCALL
+ EPILOG_BRANCH_REG x9
+
+NESTED_END ThePreStub, _TEXT
+
+// ------------------------------------------------------------------
+// ThePreStubPatch()
+
+LEAF_ENTRY ThePreStubPatch, _TEXT
+ nop
+.globl C_FUNC(ThePreStubPatchLabel)
+C_FUNC(ThePreStubPatchLabel):
+ ret lr
+LEAF_END ThePreStubPatch, _TEXT
+
+
+// ------------------------------------------------------------------
+// void ResolveWorkerAsmStub(args in regs x0-x7 & stack, x11:IndirectionCellAndFlags, x12:DispatchToken)
+//
+// The stub dispatch thunk which transfers control to VSD_ResolveWorker.
+NESTED_ENTRY ResolveWorkerAsmStub, _TEXT, NoHandler
+
+ PROLOG_WITH_TRANSITION_BLOCK
+
+ add x0, sp, #__PWTB_TransitionBlock // pTransitionBlock
+ and x1, x11, #-4 // Indirection cell
+ mov x2, x12 // DispatchToken
+ and x3, x11, #3 // flag
+ bl C_FUNC(VSD_ResolveWorker)
+ mov x9, x0
+
+ EPILOG_WITH_TRANSITION_BLOCK_TAILCALL
+
+ EPILOG_BRANCH_REG x9
+
+NESTED_END ResolveWorkerAsmStub, _TEXT
+
+NESTED_ENTRY ResolveWorkerChainLookupAsmStub, _TEXT, NoHandler
+
+ // ARMSTUB TODO: implement chained lookup
+ b C_FUNC(ResolveWorkerAsmStub)
+
+NESTED_END ResolveWorkerChainLookupAsmStub, _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
+.endmacro
+
+// WRITE_BARRIER_END
+//
+// The partner to WRITE_BARRIER_ENTRY, used like NESTED_END.
+//
+.macro WRITE_BARRIER_END name
+ LEAF_END_MARKED \name, _TEXT
+.endmacro
+
+// void JIT_ByRefWriteBarrier
+// On entry:
+// x13 : the source address (points to object reference to write)
+// x14 : the destination address (object reference written here)
+//
+// On exit:
+// x12 : trashed
+// x13 : incremented by 8
+// x14 : incremented by 8
+// x15 : trashed
+//
+WRITE_BARRIER_ENTRY JIT_ByRefWriteBarrier
+
+ ldr x15, [x13], 8
+ b C_FUNC(JIT_CheckedWriteBarrier)
+
+WRITE_BARRIER_END JIT_ByRefWriteBarrier
+
+//-----------------------------------------------------------------------------
+// Simple WriteBarriers
+// void JIT_CheckedWriteBarrier(Object** dst, Object* src)
+// On entry:
+// x14 : the destination address (LHS of the assignment)
+// x15 : the object reference (RHS of the assignment)
+//
+// On exit:
+// x12 : trashed
+// x14 : incremented by 8
+// x15 : trashed
+//
+WRITE_BARRIER_ENTRY JIT_CheckedWriteBarrier
+ // ARM64TODO: Temporary indirect access till support for :lo12:symbol is added
+ ldr x12, =g_lowest_address
+ ldr x12, [x12]
+ cmp x14, x12
+ blt LOCAL_LABEL(NotInHeap)
+
+ // ARM64TODO: Temporary indirect access till support for :lo12:symbol is added
+ ldr x12, =g_highest_address
+ ldr x12, [x12]
+ cmp x14, x12
+ blt C_FUNC(JIT_WriteBarrier)
+
+LOCAL_LABEL(NotInHeap):
+ str x15, [x14], 8
+ ret lr
+WRITE_BARRIER_END JIT_CheckedWriteBarrier
+
+// void JIT_WriteBarrier(Object** dst, Object* src)
+// On entry:
+// x14 : the destination address (LHS of the assignment)
+// x15 : the object reference (RHS of the assignment)
+//
+// On exit:
+// x12 : trashed
+// x14 : incremented by 8
+// x15 : trashed
+//
+WRITE_BARRIER_ENTRY JIT_WriteBarrier
+ dmb ST
+ str x15, [x14], 8
+
+ // Branch to Exit if the reference is not in the Gen0 heap
+ //
+ // ARM64TODO: Temporary indirect access till support for :lo12:symbol is added
+ ldr x12, =g_ephemeral_low
+ ldr x12, [x12]
+ cmp x15, x12
+ blt LOCAL_LABEL(Exit)
+
+ // ARM64TODO: Temporary indirect access till support for :lo12:symbol is added
+ ldr x12, =g_ephemeral_high
+ ldr x12, [x12]
+ cmp x15, x12
+ bgt LOCAL_LABEL(Exit)
+
+ // Check if we need to update the card table
+ // ARM64TODO: Temporary indirect access till support for :lo12:symbol is added
+ ldr x12, =g_card_table
+ ldr x12, [x12]
+ add x15, x12, x14, lsr #11
+ ldrb w12, [x15]
+ cmp x12, 0xFF
+ beq LOCAL_LABEL(Exit)
+
+LOCAL_LABEL(UpdateCardTable):
+ mov x12, 0xFF
+ strb w12, [x15]
+LOCAL_LABEL(Exit):
+ ret lr
+WRITE_BARRIER_END JIT_WriteBarrier
+
+// ------------------------------------------------------------------
+// Start of the writeable code region
+LEAF_ENTRY JIT_PatchedCodeStart, _TEXT
+ ret lr
+LEAF_END JIT_PatchedCodeStart, _TEXT
+
+// ------------------------------------------------------------------
+// End of the writeable code region
+LEAF_ENTRY JIT_PatchedCodeLast, _TEXT
+ ret lr
+LEAF_END JIT_PatchedCodeLast, _TEXT
+
+//------------------------------------------------
+// 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:
+//
+// x0 = "this" pointer
+// x12 = Address of thunk
+
+NESTED_ENTRY VirtualMethodFixupStub, _TEXT, NoHandler
+
+ // Save arguments and return address
+ PROLOG_SAVE_REG_PAIR_INDEXED fp, lr, #-144
+ SAVE_ARGUMENT_REGISTERS sp, 16
+ SAVE_FLOAT_ARGUMENT_REGISTERS sp, 80
+
+ // Refer to ZapImportVirtualThunk::Save
+ // for details on this.
+ //
+ // Move the thunk start address in x1
+ mov x1, x12
+
+ // Call the helper in the VM to perform the actual fixup
+ // and tell us where to tail call. x0 already contains
+ // the this pointer.
+ bl C_FUNC(VirtualMethodFixupWorker)
+ // On return, x0 contains the target to tailcall to
+ mov x12, x0
+
+ // pop the stack and restore original register state
+ RESTORE_ARGUMENT_REGISTERS sp, 16
+ RESTORE_FLOAT_ARGUMENT_REGISTERS sp, 80
+ EPILOG_RESTORE_REG_PAIR_INDEXED fp, lr, #144
+
+ PATCH_LABEL VirtualMethodFixupPatchLabel
+
+ // and tailcall to the actual method
+ EPILOG_BRANCH_REG x12
+
+NESTED_END VirtualMEthodFixupStub, _TEXT
+
+//------------------------------------------------
+// ExternalMethodFixupStub
+//
+// In NGEN images, calls to cross-module external methods initially
+// 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 the
+// 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:
+//
+// x12 = Address of thunk
+
+NESTED_ENTRY ExternalMethodFixupStub, _TEXT, NoHandler
+
+ PROLOG_WITH_TRANSITION_BLOCK
+
+ add x0, sp, #__PWTB_TransitionBlock // pTransitionBlock
+ mov x1, x12 // pThunk
+
+ bl C_FUNC(ExternalMethodFixupWorker)
+
+ // mov the address we patched to in x12 so that we can tail call to it
+ mov x12, x0
+
+ EPILOG_WITH_TRANSITION_BLOCK_TAILCALL
+ PATCH_LABEL ExternalMethodFixupPatchLabel
+ EPILOG_BRANCH_REG x12
+
+NESTED_END ExternalMethodFixupStub, _TEXT
+
+// void SinglecastDelegateInvokeStub(Delegate *pThis)
+LEAF_ENTRY SinglecastDelegateInvokeStub, _TEXT
+ cmp x0, #0
+ beq LOCAL_LABEL(LNullThis)
+
+ ldr x16, [x0, #DelegateObject___methodPtr]
+ ldr x0, [x0, #DelegateObject___target]
+
+ br x16
+
+LOCAL_LABEL(LNullThis):
+ mov x0, #CORINFO_NullReferenceException_ASM
+ b C_FUNC(JIT_InternalThrow)
+
+LEAF_END SinglecastDelegateInvokeStub, _TEXT
+
+//
+// x12 = UMEntryThunk*
+//
+NESTED_ENTRY TheUMEntryPrestub, _TEXT, UnhandledExceptionHandlerUnix
+
+ // Save arguments and return address
+ PROLOG_SAVE_REG_PAIR_INDEXED fp, lr, #-144
+ SAVE_ARGUMENT_REGISTERS sp, 16
+ SAVE_FLOAT_ARGUMENT_REGISTERS sp, 80
+
+ mov x0, x12
+ bl C_FUNC(TheUMEntryPrestubWorker)
+
+ // save real target address in x12.
+ mov x12, x0
+
+ // pop the stack and restore original register state
+ RESTORE_ARGUMENT_REGISTERS sp, 16
+ RESTORE_FLOAT_ARGUMENT_REGISTERS sp, 80
+ EPILOG_RESTORE_REG_PAIR_INDEXED fp, lr, #144
+
+ // and tailcall to the actual method
+ EPILOG_BRANCH_REG x12
+
+NESTED_END TheUMEntryPrestub, _TEXT
+
+//
+// x12 = UMEntryThunk*
+//
+NESTED_ENTRY UMThunkStub, _TEXT, UnhandledExceptionHandlerUnix
+
+ // Save arguments and return address
+ PROLOG_SAVE_REG_PAIR_INDEXED fp, lr, #-96 // 64 for regArgs, 8 for x19 & 8 for x12
+ // save callee saved reg x19. x19 is used in the method to store thread*
+ PROLOG_SAVE_REG x19, #88
+
+ SAVE_ARGUMENT_REGISTERS sp, 16
+
+#define UMThunkStub_HiddenArg 80 // offset of saved UMEntryThunk *
+#define UMThunkStub_StackArgs 96 // offset of original stack args (total size of UMThunkStub frame)
+
+ // save UMEntryThunk*
+ str x12, [sp, #UMThunkStub_HiddenArg]
+
+ // assuming GetThread does not clobber FP Args
+ bl C_FUNC(GetThread)
+ cbz x0, LOCAL_LABEL(UMThunkStub_DoThreadSetup)
+
+LOCAL_LABEL(UMThunkStub_HaveThread):
+ mov x19, x0 // x19 = Thread *
+
+ mov x9, 1
+ // m_fPreemptiveGCDisabled is 4 byte field so using 32-bit variant
+ str w9, [x19, #Thread__m_fPreemptiveGCDisabled]
+
+ ldr x2, =g_TrapReturningThreads
+ ldr x3, [x2]
+ // assuming x0 contains Thread* before jumping to UMThunkStub_DoTrapReturningThreads
+ cbnz x3, LOCAL_LABEL(UMThunkStub_DoTrapReturningThreads)
+
+LOCAL_LABEL(UMThunkStub_InCooperativeMode):
+ ldr x12, [fp, #UMThunkStub_HiddenArg] // x12 = UMEntryThunk*
+
+ ldr x0, [x19, #Thread__m_pDomain]
+
+ // m_dwDomainId is 4 bytes so using 32-bit variant
+ ldr w1, [x12, #UMEntryThunk__m_dwDomainId]
+ ldr w0, [x0, #AppDomain__m_dwId]
+ cmp w0, w1
+ bne LOCAL_LABEL(UMThunkStub_WrongAppDomain)
+
+ ldr x3, [x12, #UMEntryThunk__m_pUMThunkMarshInfo] // x3 = m_pUMThunkMarshInfo
+
+ // m_cbActualArgSize is UINT32 and hence occupies 4 bytes
+ ldr w2, [x3, #UMThunkMarshInfo__m_cbActualArgSize] // w2 = Stack arg bytes
+ cbz w2, LOCAL_LABEL(UMThunkStub_RegArgumentsSetup)
+
+ // extend to 64-bits
+ uxtw x2, w2
+
+ // Source pointer
+ add x0, fp, #UMThunkStub_StackArgs
+
+ // move source pointer to end of Stack Args
+ add x0, x0, x2
+
+ // Count of stack slot pairs to copy (divide by 16)
+ lsr x1, x2, #4
+
+ // Is there an extra stack slot (can happen when stack arg bytes not multiple of 16)
+ and x2, x2, #8
+
+ // If yes then start source pointer from 16 byte aligned stack slot
+ add x0, x0, x2
+
+ // increment stack slot pair count by 1 if x2 is not zero
+ add x1, x1, x2, LSR #3
+
+LOCAL_LABEL(UMThunkStub_StackLoop):
+ ldp x4, x5, [x0, #-16]! // pre-Index
+ stp x4, x5, [sp, #-16]! // pre-Index
+ subs x1, x1, #1
+ bne LOCAL_LABEL(UMThunkStub_StackLoop)
+
+LOCAL_LABEL(UMThunkStub_RegArgumentsSetup):
+ ldr x16, [x3, #UMThunkMarshInfo__m_pILStub]
+
+ RESTORE_ARGUMENT_REGISTERS fp, 16
+
+ blr x16
+
+LOCAL_LABEL(UMThunkStub_PostCall):
+ mov x4, 0
+ // m_fPreemptiveGCDisabled is 4 byte field so using 32-bit variant
+ str w4, [x19, #Thread__m_fPreemptiveGCDisabled]
+
+ EPILOG_STACK_RESTORE
+ EPILOG_RESTORE_REG x19, #88
+ EPILOG_RESTORE_REG_PAIR_INDEXED fp, lr, #96
+
+ EPILOG_RETURN
+
+LOCAL_LABEL(UMThunkStub_DoThreadSetup):
+ sub sp, sp, #SIZEOF__FloatArgumentRegisters
+ SAVE_FLOAT_ARGUMENT_REGISTERS sp, 0
+ bl C_FUNC(CreateThreadBlockThrow)
+ RESTORE_FLOAT_ARGUMENT_REGISTERS sp, 0
+ add sp, sp, #SIZEOF__FloatArgumentRegisters
+ b LOCAL_LABEL(UMThunkStub_HaveThread)
+
+LOCAL_LABEL(UMThunkStub_DoTrapReturningThreads):
+ sub sp, sp, #SIZEOF__FloatArgumentRegisters
+ SAVE_FLOAT_ARGUMENT_REGISTERS sp, 0
+ // x0 already contains Thread* pThread
+ // UMEntryThunk* pUMEntry
+ ldr x1, [fp, #UMThunkStub_HiddenArg]
+ bl C_FUNC(UMThunkStubRareDisableWorker)
+ RESTORE_FLOAT_ARGUMENT_REGISTERS sp, 0
+ add sp, sp, #SIZEOF__FloatArgumentRegisters
+ b LOCAL_LABEL(UMThunkStub_InCooperativeMode)
+
+LOCAL_LABEL(UMThunkStub_WrongAppDomain):
+ // Saving FP Args as this is read by UM2MThunk_WrapperHelper
+ sub sp, sp, #SIZEOF__FloatArgumentRegisters
+ SAVE_FLOAT_ARGUMENT_REGISTERS sp, 0
+
+ // UMEntryThunk* pUMEntry
+ ldr x0, [fp, #UMThunkStub_HiddenArg]
+
+ // void * pArgs
+ add x2, fp, #16
+
+ // remaining arguments are unused
+ bl C_FUNC(UM2MDoADCallBack)
+
+ // restore integral return value
+ ldr x0, [fp, #16]
+
+ // restore FP or HFA return value
+ RESTORE_FLOAT_ARGUMENT_REGISTERS sp, 0
+
+ b LOCAL_LABEL(UMThunkStub_PostCall)
+
+NESTED_END UMThunkStub, _TEXT
+
+
+// UM2MThunk_WrapperHelper(void *pThunkArgs, // x0
+// int cbStackArgs, // x1 (unused)
+// void *pAddr, // x2 (unused)
+// UMEntryThunk *pEntryThunk,// x3
+// Thread *pThread) // x4
+
+// pThunkArgs points to the argument registers pushed on the stack by UMThunkStub
+
+NESTED_ENTRY UM2MThunk_WrapperHelper, _TEXT, NoHandler
+
+ PROLOG_SAVE_REG_PAIR_INDEXED fp, lr, #-32
+ PROLOG_SAVE_REG x19, #16
+
+
+ // save pThunkArgs in non-volatile reg. It is required after return from call to ILStub
+ mov x19, x0
+
+ // ARM64TODO - Is this required by ILStub
+ mov x12, x3 //// x12 = UMEntryThunk *
+
+ //
+ // Note that layout of the arguments is given by UMThunkStub frame
+ //
+ ldr x3, [x3, #UMEntryThunk__m_pUMThunkMarshInfo]
+
+ // m_cbActualArgSize is 4-byte field
+ ldr w2, [x3, #UMThunkMarshInfo__m_cbActualArgSize]
+ cbz w2, LOCAL_LABEL(UM2MThunk_WrapperHelper_RegArgumentsSetup)
+
+ // extend to 64- bits
+ uxtw x2, w2
+
+ // Source pointer. Subtracting 16 bytes due to fp & lr
+ add x6, x0, #(UMThunkStub_StackArgs-16)
+
+ // move source ptr to end of Stack Args
+ add x6, x6, x2
+
+ // Count of stack slot pairs to copy (divide by 16)
+ lsr x1, x2, #4
+
+ // Is there an extra stack slot? (can happen when stack arg bytes not multiple of 16)
+ and x2, x2, #8
+
+ // If yes then start source pointer from 16 byte aligned stack slot
+ add x6, x6, x2
+
+ // increment stack slot pair count by 1 if x2 is not zero
+ add x1, x1, x2, LSR #3
+
+LOCAL_LABEL(UM2MThunk_WrapperHelper_StackLoop):
+ ldp x4, x5, [x6, #-16]!
+ stp x4, x5, [sp, #-16]!
+ subs x1, x1, #1
+ bne LOCAL_LABEL(UM2MThunk_WrapperHelper_StackLoop)
+
+LOCAL_LABEL(UM2MThunk_WrapperHelper_RegArgumentsSetup):
+ ldr x16, [x3, #(UMThunkMarshInfo__m_pILStub)]
+
+ // reload floating point registers
+ RESTORE_FLOAT_ARGUMENT_REGISTERS x0, -1 * (SIZEOF__FloatArgumentRegisters + 16)
+
+ // reload argument registers
+ RESTORE_ARGUMENT_REGISTERS x0, 0
+
+ blr x16
+
+ // save integral return value
+ str x0, [x19]
+ // save FP/HFA return values
+ SAVE_FLOAT_ARGUMENT_REGISTERS x19, -1 * (SIZEOF__FloatArgumentRegisters + 16)
+
+ EPILOG_STACK_RESTORE
+ EPILOG_RESTORE_REG x19, #16
+ EPILOG_RESTORE_REG_PAIR_INDEXED fp, lr, #32
+ EPILOG_RETURN
+
+NESTED_END UM2MThunk_WrapperHelper, _TEXT
+
+
+
+// ------------------------------------------------------------------
+// Redirection Stub for GC in fully interruptible method
+//GenerateRedirectedHandledJITCaseStub GCThreadControl
+// ------------------------------------------------------------------
+//GenerateRedirectedHandledJITCaseStub DbgThreadControl
+// ------------------------------------------------------------------
+//GenerateRedirectedHandledJITCaseStub UserSuspend
+// ------------------------------------------------------------------
+//GenerateRedirectedHandledJITCaseStub YieldTask
+
+#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
+
+ // Using below prolog instead of PROLOG_SAVE_REG_PAIR fp,lr, #-16
+ // is intentional. Above statement would also emit instruction to save
+ // sp in fp. If sp is saved in fp in prolog then it is not expected that fp can change in the body
+ // of method. However, this method needs to be able to change fp before calling funclet.
+ // This is required to access locals in funclet.
+ PROLOG_SAVE_REG_PAIR_INDEXED x19,x20, #-16
+ PROLOG_SAVE_REG fp, #0
+ PROLOG_SAVE_REG lr, #8
+
+ // On entry:
+ //
+ // X0 = throwable
+ // X1 = PC to invoke
+ // X2 = address of X19 register in CONTEXT record// used to restore the non-volatile registers of CrawlFrame
+ // X3 = address of the location where the SP of funclet's caller (i.e. this helper) should be saved.
+ //
+ // Save the SP of this function
+ str fp, [x3]
+
+ ldr fp, [x2, #80] // offset of fp in CONTEXT relative to X19
+
+ // Invoke the funclet
+ blr x1
+ nop
+
+ EPILOG_RESTORE_REG_PAIR_INDEXED fp, lr, #16
+ EPILOG_RETURN
+
+NESTED_END CallEHFunclet, _TEXT
+
+// This helper enables us to call into a filter funclet by passing it the CallerSP to lookup the
+// frame pointer for accessing the locals in the parent method.
+NESTED_ENTRY CallEHFilterFunclet, _TEXT, NoHandler
+
+ PROLOG_SAVE_REG_PAIR_INDEXED fp, lr, #-16
+
+ // On entry:
+ //
+ // X0 = throwable
+ // X1 = SP of the caller of the method/funclet containing the filter
+ // X2 = PC to invoke
+ // X3 = address of the location where the SP of funclet's caller (i.e. this helper) should be saved.
+ //
+ // Save the SP of this function
+ str fp, [x3]
+ // Invoke the filter funclet
+ blr x2
+
+ EPILOG_RESTORE_REG_PAIR_INDEXED fp, lr, #16
+ EPILOG_RETURN
+
+NESTED_END CallEHFilterFunclet, _TEXT
+
+#define FaultingExceptionFrame_StackAlloc (SIZEOF__GSCookie + SIZEOF__FaultingExceptionFrame)
+#define FaultingExceptionFrame_FrameOffset SIZEOF__GSCookie
+
+.macro GenerateRedirectedStubWithFrame stub, target
+
+ //
+ // This is the primary function to which execution will be redirected to.
+ //
+ NESTED_ENTRY \stub, _TEXT, NoHandler
+
+ //
+ // IN: lr: original IP before redirect
+ //
+
+ PROLOG_SAVE_REG_PAIR_INDEXED fp, lr, #-16
+ PROLOG_STACK_ALLOC FaultingExceptionFrame_StackAlloc
+
+ // At this point, the stack maybe misaligned if the thread abort was asynchronously
+ // triggered in the prolog or epilog of the managed method. For such a case, we must
+ // align the stack before calling into the VM.
+ //
+ // Runtime check for 16-byte alignment.
+ mov x0, sp
+ and x0, x0, #15
+ sub sp, sp, x0
+
+ // Save pointer to FEF for GetFrameFromRedirectedStubStackFrame
+ add x19, sp, #FaultingExceptionFrame_FrameOffset
+
+ // Prepare to initialize to NULL
+ mov x1,#0
+ str x1, [x19]// Initialize vtbl (it is not strictly necessary)
+ str x1, [x19, #FaultingExceptionFrame__m_fFilterExecuted]// Initialize BOOL for personality routine
+
+ mov x0, x19 // move the ptr to FEF in X0
+
+ bl C_FUNC(\target)
+
+ // Target should not return.
+ EMIT_BREAKPOINT
+
+ NESTED_END \stub, _TEXT
+
+.endmacro
+
+
+// ------------------------------------------------------------------
+//
+// Helpers for async (NullRef, AccessViolation) exceptions
+//
+
+NESTED_ENTRY NakedThrowHelper2, _TEXT ,FixContextHandler
+ PROLOG_SAVE_REG_PAIR_INDEXED fp,lr, #-16
+
+ // On entry:
+ //
+ // X0 = Address of FaultingExceptionFrame
+ bl C_FUNC(LinkFrameAndThrow)
+
+ // Target should not return.
+ EMIT_BREAKPOINT
+
+NESTED_END NakedThrowHelper2, _TEXT
+
+GenerateRedirectedStubWithFrame NakedThrowHelper, NakedThrowHelper2
+
+#ifdef FEATURE_READYTORUN
+
+NESTED_ENTRY DelayLoad_MethodCall_FakeProlog, _TEXT, NoHandler
+DelayLoad_MethodCall:
+ .global DelayLoad_MethodCall
+
+ EMIT_BREAKPOINT
+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
+
+ EMIT_BREAKPOINT
+
+ NESTED_END DelayLoad_Helper\suffix\()_FakeProlog, _TEXT
+.endm
+
+DynamicHelper DynamicHelperFrameFlags_Default
+DynamicHelper DynamicHelperFrameFlags_ObjectArg, _Obj
+DynamicHelper DynamicHelperFrameFlags_ObjectArg | DynamicHelperFrameFlags_ObjectArg2, _ObjObj
+
+#endif