1 // Licensed to the .NET Foundation under one or more agreements.
2 // The .NET Foundation licenses this file to you under the MIT license.
3 // See the LICENSE file in the project root for more information.
5 #include "asmconstants.h"
6 #include "unixasmmacros.inc"
8 // LPVOID __stdcall GetCurrentIP(void)//
9 LEAF_ENTRY GetCurrentIP, _TEXT
12 LEAF_END GetCurrentIP, _TEXT
14 // LPVOID __stdcall GetCurrentSP(void)//
15 LEAF_ENTRY GetCurrentSP, _TEXT
18 LEAF_END GetCurrentSP, _TEXT
20 //-----------------------------------------------------------------------------
21 // This routine captures the machine state. It is used by helper method frame
22 //-----------------------------------------------------------------------------
23 //void LazyMachStateCaptureState(struct LazyMachState *pState)//
24 LEAF_ENTRY LazyMachStateCaptureState, _TEXT
25 // marks that this is not yet valid
27 str w1, [x0, #MachState__isValid]
29 str lr, [x0, #LazyMachState_captureIp]
31 // str instruction does not save sp register directly so move to temp register
33 str x1, [x0, #LazyMachState_captureSp]
35 // save non-volatile registers that can contain object references
36 add x1, x0, #LazyMachState_captureX19_X29
37 stp x19, x20, [x1, #(16*0)]
38 stp x21, x22, [x1, #(16*1)]
39 stp x23, x24, [x1, #(16*2)]
40 stp x25, x26, [x1, #(16*3)]
41 stp x27, x28, [x1, #(16*4)]
42 str x29, [x1, #(16*5)]
45 LEAF_END LazyMachStateCaptureState, _TEXT
48 // If a preserved register were pushed onto the stack between
49 // the managed caller and the H_M_F, ptrX19_X29 will point to its
50 // location on the stack and it would have been updated on the
51 // stack by the GC already and it will be popped back into the
52 // appropriate register when the appropriate epilog is run.
54 // Otherwise, the register is preserved across all the code
55 // in this HCALL or FCALL, so we need to update those registers
56 // here because the GC will have updated our copies in the
59 // So, if ptrX19_X29 points into the MachState, we need to update
60 // the register here. That's what this macro does.
62 .macro RestoreRegMS regIndex, reg
65 // x0 = address of MachState
67 // $regIndex: Index of the register (x19-x28). For x19, index is 19.
68 //For x20, index is 20, and so on.
70 // $reg: Register name (e.g. x19, x20, etc)
72 // Get the address of the specified captured register from machine state
73 add x2, x0, #(MachState__captureX19_X29 + ((\regIndex-19)*8))
75 // Get the content of specified preserved register pointer from machine state
76 ldr x3, [x0, #(MachState__ptrX19_X29 + ((\regIndex-19)*8))]
79 bne LOCAL_LABEL(NoRestore_\reg)
81 LOCAL_LABEL(NoRestore_\reg):
85 // EXTERN_C int __fastcall HelperMethodFrameRestoreState(
86 // INDEBUG_COMMA(HelperMethodFrame *pFrame)
89 LEAF_ENTRY HelperMethodFrameRestoreState, _TEXT
95 // If machine state is invalid, then simply exit
96 ldr w1, [x0, #MachState__isValid]
112 // Its imperative that the return value of HelperMethodFrameRestoreState is zero
113 // as it is used in the state machine to loop until it becomes zero.
114 // Refer to HELPER_METHOD_FRAME_END macro for details.
118 LEAF_END HelperMethodFrameRestoreState, _TEXT
120 // ------------------------------------------------------------------
121 // The call in ndirect import precode points to this function.
122 NESTED_ENTRY NDirectImportThunk, _TEXT, NoHandler
124 PROLOG_SAVE_REG_PAIR_INDEXED fp, lr, -160
125 SAVE_ARGUMENT_REGISTERS sp, 16
126 SAVE_FLOAT_ARGUMENT_REGISTERS sp, 88
129 bl NDirectImportWorker
132 // pop the stack and restore original register state
133 RESTORE_FLOAT_ARGUMENT_REGISTERS sp, 88
134 RESTORE_ARGUMENT_REGISTERS sp, 16
135 EPILOG_RESTORE_REG_PAIR_INDEXED fp, lr, 160
137 // If we got back from NDirectImportWorker, the MD has been successfully
138 // linked. Proceed to execute the original DLL call.
139 EPILOG_BRANCH_REG x12
141 NESTED_END NDirectImportThunk, _TEXT
143 // ------------------------------------------------------------------
144 // The call in fixup precode initally points to this function.
145 // The pupose of this function is to load the MethodDesc and forward the call to prestub.
146 NESTED_ENTRY PrecodeFixupThunk, _TEXT, NoHandler
147 // x12 = FixupPrecode *
151 // Inline computation done by FixupPrecode::GetMethodDesc()
152 ldrb w13, [x12, #Offset_PrecodeChunkIndex] //m_PrecodeChunkIndex
153 ldrb w14, [x12, #Offset_MethodDescChunkIndex] // m_MethodDescChunkIndex
155 add x12, x12, w13, uxtw #FixupPrecode_ALIGNMENT_SHIFT_1
156 add x13, x12, w13, uxtw #FixupPrecode_ALIGNMENT_SHIFT_2
157 ldr x13, [x13, #SIZEOF__FixupPrecode]
158 add x12, x13, w14, uxtw #MethodDesc_ALIGNMENT_SHIFT
161 NESTED_END PrecodeFixupThunk, _TEXT
162 // ------------------------------------------------------------------
164 NESTED_ENTRY ThePreStub, _TEXT, NoHandler
166 PROLOG_WITH_TRANSITION_BLOCK
168 add x0, sp, #__PWTB_TransitionBlock // pTransitionBlock
169 mov x1, METHODDESC_REGISTER // pMethodDesc
175 EPILOG_WITH_TRANSITION_BLOCK_TAILCALL
178 NESTED_END ThePreStub, _TEXT
180 // ------------------------------------------------------------------
183 LEAF_ENTRY ThePreStubPatch, _TEXT
185 .globl C_FUNC(ThePreStubPatchLabel)
186 C_FUNC(ThePreStubPatchLabel):
188 LEAF_END ThePreStubPatch, _TEXT
191 //-----------------------------------------------------------------------------
192 // The following Macros help in WRITE_BARRIER Implemetations
193 // WRITE_BARRIER_ENTRY
195 // Declare the start of a write barrier function. Use similarly to NESTED_ENTRY. This is the only legal way
196 // to declare a write barrier function.
198 .macro WRITE_BARRIER_ENTRY name
199 LEAF_ENTRY \name, _TEXT
204 // The partner to WRITE_BARRIER_ENTRY, used like NESTED_END.
206 .macro WRITE_BARRIER_END name
207 LEAF_END_MARKED \name, _TEXT
210 // ------------------------------------------------------------------
211 // Start of the writeable code region
212 LEAF_ENTRY JIT_PatchedCodeStart, _TEXT
214 LEAF_END JIT_PatchedCodeStart, _TEXT
216 // void JIT_UpdateWriteBarrierState(bool skipEphemeralCheck)
218 // Update shadow copies of the various state info required for barrier
220 // State info is contained in a literal pool at the end of the function
221 // Placed in text section so that it is close enough to use ldr literal and still
222 // be relocatable. Eliminates need for PREPARE_EXTERNAL_VAR in hot code.
224 // Align and group state info together so it fits in a single cache line
225 // and each entry can be written atomically
227 WRITE_BARRIER_ENTRY JIT_UpdateWriteBarrierState
228 PROLOG_SAVE_REG_PAIR_INDEXED fp, lr, -16
230 // x0-x7 will contain intended new state
231 // x8 will preserve skipEphemeralCheck
232 // x12 will be used for pointers
236 PREPARE_EXTERNAL_VAR g_card_table, x12
239 #ifdef FEATURE_MANUALLY_MANAGED_CARD_BUNDLES
240 PREPARE_EXTERNAL_VAR g_card_bundle_table, x12
244 #ifdef WRITE_BARRIER_CHECK
245 PREPARE_EXTERNAL_VAR g_GCShadow, x12
249 #ifdef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
250 PREPARE_EXTERNAL_VAR g_sw_ww_table, x12
254 PREPARE_EXTERNAL_VAR g_ephemeral_low, x12
257 PREPARE_EXTERNAL_VAR g_ephemeral_high, x12
260 cbz x8, LOCAL_LABEL(EphemeralCheckEnabled)
263 LOCAL_LABEL(EphemeralCheckEnabled):
265 PREPARE_EXTERNAL_VAR g_lowest_address, x12
268 PREPARE_EXTERNAL_VAR g_highest_address, x12
272 adr x12, LOCAL_LABEL(wbs_begin)
274 stp x0, x1, [x12], 16
275 stp x2, x3, [x12], 16
276 stp x4, x5, [x12], 16
277 stp x6, x7, [x12], 16
279 EPILOG_RESTORE_REG_PAIR_INDEXED fp, lr, 16
282 // Begin patchable literal pool
283 .balign 64 // Align to power of two at least as big as patchable literal pool so that it fits optimally in cache line
284 LOCAL_LABEL(wbs_begin):
285 LOCAL_LABEL(wbs_card_table):
287 LOCAL_LABEL(wbs_card_bundle_table):
289 LOCAL_LABEL(wbs_GCShadow):
291 LOCAL_LABEL(wbs_sw_ww_table):
293 LOCAL_LABEL(wbs_ephemeral_low):
295 LOCAL_LABEL(wbs_ephemeral_high):
297 LOCAL_LABEL(wbs_lowest_address):
299 LOCAL_LABEL(wbs_highest_address):
301 WRITE_BARRIER_END JIT_UpdateWriteBarrierState
304 // ------------------------------------------------------------------
305 // End of the writeable code region
306 LEAF_ENTRY JIT_PatchedCodeLast, _TEXT
308 LEAF_END JIT_PatchedCodeLast, _TEXT
310 // void JIT_ByRefWriteBarrier
312 // x13 : the source address (points to object reference to write)
313 // x14 : the destination address (object reference written here)
317 // x13 : incremented by 8
318 // x14 : incremented by 8
320 // x17 : trashed (ip1) if FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
322 WRITE_BARRIER_ENTRY JIT_ByRefWriteBarrier
325 b C_FUNC(JIT_CheckedWriteBarrier)
327 WRITE_BARRIER_END JIT_ByRefWriteBarrier
329 //-----------------------------------------------------------------------------
330 // Simple WriteBarriers
331 // void JIT_CheckedWriteBarrier(Object** dst, Object* src)
333 // x14 : the destination address (LHS of the assignment)
334 // x15 : the object reference (RHS of the assignment)
338 // x14 : trashed (incremented by 8 to implement JIT_ByRefWriteBarrier contract)
340 // x17 : trashed (ip1) if FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
342 WRITE_BARRIER_ENTRY JIT_CheckedWriteBarrier
343 ldr x12, LOCAL_LABEL(wbs_lowest_address)
346 ldr x12, LOCAL_LABEL(wbs_highest_address)
348 // Compare against the upper bound if the previous comparison indicated
349 // that the destination address is greater than or equal to the lower
350 // bound. Otherwise, set the C flag (specified by the 0x2) so that the
351 // branch below is not taken.
352 ccmp x14, x12, #0x2, hs
354 blo C_FUNC(JIT_WriteBarrier)
356 LOCAL_LABEL(NotInHeap):
359 WRITE_BARRIER_END JIT_CheckedWriteBarrier
361 // void JIT_WriteBarrier(Object** dst, Object* src)
363 // x14 : the destination address (LHS of the assignment)
364 // x15 : the object reference (RHS of the assignment)
368 // x14 : trashed (incremented by 8 to implement JIT_ByRefWriteBarrier contract)
370 // x17 : trashed (ip1) if FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
372 WRITE_BARRIER_ENTRY JIT_WriteBarrier
375 #ifdef WRITE_BARRIER_CHECK
376 // Update GC Shadow Heap
378 // Do not perform the work if g_GCShadow is 0
379 ldr x12, LOCAL_LABEL(wbs_GCShadow)
380 cbz x12, LOCAL_LABEL(ShadowUpdateDisabled)
382 // need temporary register. Save before using.
385 // Compute address of shadow heap location:
386 // pShadow = g_GCShadow + (x14 - g_lowest_address)
387 ldr x13, LOCAL_LABEL(wbs_lowest_address)
391 // if (pShadow >= g_GCShadowEnd) goto end
392 PREPARE_EXTERNAL_VAR g_GCShadowEnd, x13
395 bhs LOCAL_LABEL(ShadowUpdateEnd)
400 // Ensure that the write to the shadow heap occurs before the read from the GC heap so that race
401 // conditions are caught by INVALIDGCVALUE.
404 // if ([x14] == x15) goto end
407 beq LOCAL_LABEL(ShadowUpdateEnd)
409 // *pShadow = INVALIDGCVALUE (0xcccccccd)
411 movk x13, #0xcccc, LSL #16
414 LOCAL_LABEL(ShadowUpdateEnd):
416 LOCAL_LABEL(ShadowUpdateDisabled):
419 #ifdef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
420 // Update the write watch table if necessary
421 ldr x12, LOCAL_LABEL(wbs_sw_ww_table)
422 cbz x12, LOCAL_LABEL(CheckCardTable)
423 add x12, x12, x14, lsr #0xc // SoftwareWriteWatch::AddressToTableByteIndexShift
425 cbnz x17, LOCAL_LABEL(CheckCardTable)
430 LOCAL_LABEL(CheckCardTable):
431 // Branch to Exit if the reference is not in the Gen0 heap
433 ldr x12, LOCAL_LABEL(wbs_ephemeral_low)
434 cbz x12, LOCAL_LABEL(SkipEphemeralCheck)
437 ldr x12, LOCAL_LABEL(wbs_ephemeral_high)
439 // Compare against the upper bound if the previous comparison indicated
440 // that the destination address is greater than or equal to the lower
441 // bound. Otherwise, set the C flag (specified by the 0x2) so that the
442 // branch to exit is taken.
443 ccmp x15, x12, #0x2, hs
445 bhi LOCAL_LABEL(Exit)
447 LOCAL_LABEL(SkipEphemeralCheck):
448 // Check if we need to update the card table
449 ldr x12, LOCAL_LABEL(wbs_card_table)
450 add x15, x12, x14, lsr #11
453 beq LOCAL_LABEL(Exit)
455 LOCAL_LABEL(UpdateCardTable):
459 #ifdef FEATURE_MANUALLY_MANAGED_CARD_BUNDLES
460 // Check if we need to update the card bundle table
461 ldr x12, LOCAL_LABEL(wbs_card_bundle_table)
462 add x15, x12, x14, lsr #21
465 beq LOCAL_LABEL(Exit)
467 LOCAL_LABEL(UpdateCardBundle):
475 WRITE_BARRIER_END JIT_WriteBarrier
477 //------------------------------------------------
478 // VirtualMethodFixupStub
480 // In NGEN images, virtual slots inherited from cross-module dependencies
481 // point to a jump thunk that calls into the following function that will
482 // call into a VM helper. The VM helper is responsible for patching up
483 // thunk, upon executing the precode, so that all subsequent calls go directly
484 // to the actual method body.
486 // This is done lazily for performance reasons.
490 // x0 = "this" pointer
491 // x12 = Address of thunk
493 NESTED_ENTRY VirtualMethodFixupStub, _TEXT, NoHandler
495 // Save arguments and return address
496 PROLOG_SAVE_REG_PAIR_INDEXED fp, lr, -160
497 SAVE_ARGUMENT_REGISTERS sp, 16
498 SAVE_FLOAT_ARGUMENT_REGISTERS sp, 88
500 // Refer to ZapImportVirtualThunk::Save
501 // for details on this.
503 // Move the thunk start address in x1
506 // Call the helper in the VM to perform the actual fixup
507 // and tell us where to tail call. x0 already contains
509 bl C_FUNC(VirtualMethodFixupWorker)
510 // On return, x0 contains the target to tailcall to
513 // pop the stack and restore original register state
514 RESTORE_ARGUMENT_REGISTERS sp, 16
515 RESTORE_FLOAT_ARGUMENT_REGISTERS sp, 88
516 EPILOG_RESTORE_REG_PAIR_INDEXED fp, lr, 160
518 PATCH_LABEL VirtualMethodFixupPatchLabel
520 // and tailcall to the actual method
521 EPILOG_BRANCH_REG x12
523 NESTED_END VirtualMEthodFixupStub, _TEXT
525 //------------------------------------------------
526 // ExternalMethodFixupStub
528 // In NGEN images, calls to cross-module external methods initially
529 // point to a jump thunk that calls into the following function that will
530 // call into a VM helper. The VM helper is responsible for patching up the
531 // thunk, upon executing the precode, so that all subsequent calls go directly
532 // to the actual method body.
534 // This is done lazily for performance reasons.
538 // x12 = Address of thunk
540 NESTED_ENTRY ExternalMethodFixupStub, _TEXT, NoHandler
542 PROLOG_WITH_TRANSITION_BLOCK
544 add x0, sp, #__PWTB_TransitionBlock // pTransitionBlock
545 mov x1, x12 // pThunk
547 bl C_FUNC(ExternalMethodFixupWorker)
549 // mov the address we patched to in x12 so that we can tail call to it
552 EPILOG_WITH_TRANSITION_BLOCK_TAILCALL
553 PATCH_LABEL ExternalMethodFixupPatchLabel
554 EPILOG_BRANCH_REG x12
556 NESTED_END ExternalMethodFixupStub, _TEXT
558 // void SinglecastDelegateInvokeStub(Delegate *pThis)
559 LEAF_ENTRY SinglecastDelegateInvokeStub, _TEXT
561 beq LOCAL_LABEL(LNullThis)
563 ldr x16, [x0, #DelegateObject___methodPtr]
564 ldr x0, [x0, #DelegateObject___target]
568 LOCAL_LABEL(LNullThis):
569 mov x0, #CORINFO_NullReferenceException_ASM
570 b C_FUNC(JIT_InternalThrow)
572 LEAF_END SinglecastDelegateInvokeStub, _TEXT
574 #ifdef FEATURE_COMINTEROP
576 #define ComCallPreStub_FrameSize (SIZEOF__GSCookie + SIZEOF__ComMethodFrame)
577 #define ComCallPreStub_FirstStackAdjust (8 + SIZEOF__ArgumentRegisters + 2 * 8) // x8, reg args , fp & lr already pushed
578 #define ComCallPreStub_StackAlloc0 (ComCallPreStub_FrameSize - ComCallPreStub_FirstStackAdjust)
579 #define ComCallPreStub_StackAlloc1 (ComCallPreStub_StackAlloc0 + SIZEOF__FloatArgumentRegisters + 8)// 8 for ErrorReturn
580 #define ComCallPreStub_StackAlloc (ComCallPreStub_StackAlloc1 + (ComCallPreStub_StackAlloc1 & 8))
582 #define ComCallPreStub_FrameOffset (ComCallPreStub_StackAlloc - (SIZEOF__ComMethodFrame - ComCallPreStub_FirstStackAdjust))
583 #define ComCallPreStub_ErrorReturnOffset0 SIZEOF__FloatArgumentRegisters
585 #define ComCallPreStub_FirstStackAdjust (ComCallPreStub_ErrorReturnOffset0 + (ComCallPreStub_ErrorReturnOffset0 & 8))
587 // ------------------------------------------------------------------
588 // COM to CLR stub called the first time a particular method is invoked.//
591 // x12 : ComCallMethodDesc* provided by prepad thunk
592 // plus user arguments in registers and on the stack
595 // tail calls to real method
597 NESTED_ENTRY ComCallPreStub, _TEXT, NoHandler
599 // Save arguments and return address
600 PROLOG_SAVE_REG_PAIR fp, lr, -ComCallPreStub_FirstStackAdjust!
601 PROLOG_STACK_ALLOC ComCallPreStub_StackAlloc
603 SAVE_ARGUMENT_REGISTERS sp, (16+ComCallPreStub_StackAlloc)
605 SAVE_FLOAT_ARGUMENT_REGISTERS sp, 0
607 str x12, [sp, #(ComCallPreStub_FrameOffset + UnmanagedToManagedFrame__m_pvDatum)]
608 add x0, sp, #(ComCallPreStub_FrameOffset)
609 add x1, sp, #(ComCallPreStub_ErrorReturnOffset)
612 cbz x0, ComCallPreStub_ErrorExit
616 // pop the stack and restore original register state
617 RESTORE_FLOAT_ARGUMENT_REGISTERS sp, 0
618 RESTORE_ARGUMENT_REGISTERS sp, (16+ComCallPreStub_StackAlloc)
620 EPILOG_STACK_FREE ComCallPreStub_StackAlloc
621 EPILOG_RESTORE_REG_PAIR fp, lr, ComCallPreStub_FirstStackAdjust!
623 // and tailcall to the actual method
624 EPILOG_BRANCH_REG x12
626 ComCallPreStub_ErrorExit
627 ldr x0, [sp, #(ComCallPreStub_ErrorReturnOffset)] // ErrorReturn
630 EPILOG_STACK_FREE ComCallPreStub_StackAlloc
631 EPILOG_RESTORE_REG_PAIR fp, lr, ComCallPreStub_FirstStackAdjust!
635 NESTED_END ComCallPreStub, _TEXT
637 // ------------------------------------------------------------------
638 // COM to CLR stub which sets up a ComMethodFrame and calls COMToCLRWorker.
641 // x12 : ComCallMethodDesc* provided by prepad thunk
642 // plus user arguments in registers and on the stack
645 // Result in x0/d0 as per the real method being called
647 NESTED_ENTRY GenericComCallStub, _TEXT, NoHandler
649 // Save arguments and return address
650 PROLOG_SAVE_REG_PAIR fp, lr, -GenericComCallStub_FirstStackAdjust!
651 PROLOG_STACK_ALLOC GenericComCallStub_StackAlloc
653 SAVE_ARGUMENT_REGISTERS sp, (16+GenericComCallStub_StackAlloc)
654 SAVE_FLOAT_ARGUMENT_REGISTERS sp, 0
656 str x12, [sp, #(GenericComCallStub_FrameOffset + UnmanagedToManagedFrame__m_pvDatum)]
657 add x1, sp, #GenericComCallStub_FrameOffset
661 EPILOG_STACK_FREE GenericComCallStub_StackAlloc
662 EPILOG_RESTORE_REG_PAIR fp, lr, GenericComCallStub_FirstStackAdjust!
666 NESTED_END GenericComCallStub, _TEXT
668 // ------------------------------------------------------------------
669 // COM to CLR stub called from COMToCLRWorker that actually dispatches to the real managed method.
672 // x0 : dwStackSlots, count of argument stack slots to copy
673 // x1 : pFrame, ComMethodFrame pushed by GenericComCallStub above
674 // x2 : pTarget, address of code to call
675 // x3 : pSecretArg, hidden argument passed to target above in x12
676 // x4 : pDangerousThis, managed 'this' reference
679 // Result in x0/d0 as per the real method being called
681 NESTED_ENTRY COMToCLRDispatchHelper, _TEXT,CallDescrWorkerUnwindFrameChainHandler
683 PROLOG_SAVE_REG_PAIR fp, lr, -16!
685 cbz x0, COMToCLRDispatchHelper_RegSetup
687 add x9, x1, #SIZEOF__ComMethodFrame
688 add x9, x9, x0, LSL #3
689 COMToCLRDispatchHelper_StackLoop
693 cbnz x0, COMToCLRDispatchHelper_StackLoop
695 COMToCLRDispatchHelper_RegSetup
697 RESTORE_FLOAT_ARGUMENT_REGISTERS x1, -1 * GenericComCallStub_FrameOffset
704 ldp x2, x3, [x1, #(SIZEOF__ComMethodFrame - SIZEOF__ArgumentRegisters + 16)]
705 ldp x4, x5, [x1, #(SIZEOF__ComMethodFrame - SIZEOF__ArgumentRegisters + 32)]
706 ldp x6, x7, [x1, #(SIZEOF__ComMethodFrame - SIZEOF__ArgumentRegisters + 48)]
707 ldr x8, [x1, #(SIZEOF__ComMethodFrame - SIZEOF__ArgumentRegisters - 8)]
709 ldr x1, [x1, #(SIZEOF__ComMethodFrame - SIZEOF__ArgumentRegisters + 8)]
714 EPILOG_RESTORE_REG_PAIR fp, lr, 16!
717 NESTED_END COMToCLRDispatchHelper, _TEXT
719 #endif // FEATURE_COMINTEROP
721 // x12 = UMEntryThunk*
723 NESTED_ENTRY TheUMEntryPrestub, _TEXT, UnhandledExceptionHandlerUnix
725 // Save arguments and return address
726 PROLOG_SAVE_REG_PAIR_INDEXED fp, lr, -160
727 SAVE_ARGUMENT_REGISTERS sp, 16
728 SAVE_FLOAT_ARGUMENT_REGISTERS sp, 88
731 bl C_FUNC(TheUMEntryPrestubWorker)
733 // save real target address in x12.
736 // pop the stack and restore original register state
737 RESTORE_ARGUMENT_REGISTERS sp, 16
738 RESTORE_FLOAT_ARGUMENT_REGISTERS sp, 88
739 EPILOG_RESTORE_REG_PAIR_INDEXED fp, lr, 160
741 // and tailcall to the actual method
742 EPILOG_BRANCH_REG x12
744 NESTED_END TheUMEntryPrestub, _TEXT
747 // x12 = UMEntryThunk*
749 NESTED_ENTRY UMThunkStub, _TEXT, UnhandledExceptionHandlerUnix
751 // Save arguments and return address
752 PROLOG_SAVE_REG_PAIR_INDEXED fp, lr, -112 // 72 for regArgs, 8 for x19 & 8 for x12
753 // save callee saved reg x19. x19 is used in the method to store thread*
754 PROLOG_SAVE_REG x19, 96
756 SAVE_ARGUMENT_REGISTERS sp, 16
758 #define UMThunkStub_HiddenArg 88 // offset of saved UMEntryThunk *
759 #define UMThunkStub_StackArgs 112 // offset of original stack args (total size of UMThunkStub frame)
761 // save UMEntryThunk*
762 str x12, [sp, #UMThunkStub_HiddenArg]
764 // assuming GetThread does not clobber FP Args
766 cbz x0, LOCAL_LABEL(UMThunkStub_DoThreadSetup)
768 LOCAL_LABEL(UMThunkStub_HaveThread):
769 mov x19, x0 // x19 = Thread *
772 // m_fPreemptiveGCDisabled is 4 byte field so using 32-bit variant
773 str w9, [x19, #Thread__m_fPreemptiveGCDisabled]
775 PREPARE_EXTERNAL_VAR g_TrapReturningThreads, x2
777 // assuming x0 contains Thread* before jumping to UMThunkStub_DoTrapReturningThreads
778 cbnz x3, LOCAL_LABEL(UMThunkStub_DoTrapReturningThreads)
780 LOCAL_LABEL(UMThunkStub_InCooperativeMode):
781 ldr x12, [fp, #UMThunkStub_HiddenArg] // x12 = UMEntryThunk*
782 ldr x3, [x12, #UMEntryThunk__m_pUMThunkMarshInfo] // x3 = m_pUMThunkMarshInfo
784 // m_cbActualArgSize is UINT32 and hence occupies 4 bytes
785 ldr w2, [x3, #UMThunkMarshInfo__m_cbActualArgSize] // w2 = Stack arg bytes
786 cbz w2, LOCAL_LABEL(UMThunkStub_RegArgumentsSetup)
792 add x0, fp, #UMThunkStub_StackArgs
794 // move source pointer to end of Stack Args
797 // Count of stack slot pairs to copy (divide by 16)
800 // Is there an extra stack slot (can happen when stack arg bytes not multiple of 16)
803 // If yes then start source pointer from 16 byte aligned stack slot
806 // increment stack slot pair count by 1 if x2 is not zero
807 add x1, x1, x2, LSR #3
809 LOCAL_LABEL(UMThunkStub_StackLoop):
810 ldp x4, x5, [x0, #-16]! // pre-Index
811 stp x4, x5, [sp, #-16]! // pre-Index
813 bne LOCAL_LABEL(UMThunkStub_StackLoop)
815 LOCAL_LABEL(UMThunkStub_RegArgumentsSetup):
816 ldr x16, [x3, #UMThunkMarshInfo__m_pILStub]
818 RESTORE_ARGUMENT_REGISTERS fp, 16
822 LOCAL_LABEL(UMThunkStub_PostCall):
824 // m_fPreemptiveGCDisabled is 4 byte field so using 32-bit variant
825 str w4, [x19, #Thread__m_fPreemptiveGCDisabled]
828 EPILOG_RESTORE_REG x19, 96
829 EPILOG_RESTORE_REG_PAIR_INDEXED fp, lr, 112
833 LOCAL_LABEL(UMThunkStub_DoThreadSetup):
834 sub sp, sp, #SIZEOF__FloatArgumentRegisters
835 SAVE_FLOAT_ARGUMENT_REGISTERS sp, 0
836 bl C_FUNC(CreateThreadBlockThrow)
837 RESTORE_FLOAT_ARGUMENT_REGISTERS sp, 0
838 add sp, sp, #SIZEOF__FloatArgumentRegisters
839 b LOCAL_LABEL(UMThunkStub_HaveThread)
841 LOCAL_LABEL(UMThunkStub_DoTrapReturningThreads):
842 sub sp, sp, #SIZEOF__FloatArgumentRegisters
843 SAVE_FLOAT_ARGUMENT_REGISTERS sp, 0
844 // x0 already contains Thread* pThread
845 // UMEntryThunk* pUMEntry
846 ldr x1, [fp, #UMThunkStub_HiddenArg]
847 bl C_FUNC(UMThunkStubRareDisableWorker)
848 RESTORE_FLOAT_ARGUMENT_REGISTERS sp, 0
849 add sp, sp, #SIZEOF__FloatArgumentRegisters
850 b LOCAL_LABEL(UMThunkStub_InCooperativeMode)
852 NESTED_END UMThunkStub, _TEXT
854 #ifdef FEATURE_HIJACK
855 // ------------------------------------------------------------------
856 // Hijack function for functions which return a scalar type or a struct (value type)
857 NESTED_ENTRY OnHijackTripThread, _TEXT, NoHandler
858 PROLOG_SAVE_REG_PAIR_INDEXED fp, lr, -144
859 // Spill callee saved registers
860 PROLOG_SAVE_REG_PAIR x19, x20, 16
861 PROLOG_SAVE_REG_PAIR x21, x22, 32
862 PROLOG_SAVE_REG_PAIR x23, x24, 48
863 PROLOG_SAVE_REG_PAIR x25, x26, 64
864 PROLOG_SAVE_REG_PAIR x27, x28, 80
866 // save any integral return value(s)
867 stp x0, x1, [sp, #96]
869 // save any FP/HFA return value(s)
870 stp d0, d1, [sp, #112]
871 stp d2, d3, [sp, #128]
876 // restore any integral return value(s)
877 ldp x0, x1, [sp, #96]
879 // restore any FP/HFA return value(s)
880 ldp d0, d1, [sp, #112]
881 ldp d2, d3, [sp, #128]
883 EPILOG_RESTORE_REG_PAIR x19, x20, 16
884 EPILOG_RESTORE_REG_PAIR x21, x22, 32
885 EPILOG_RESTORE_REG_PAIR x23, x24, 48
886 EPILOG_RESTORE_REG_PAIR x25, x26, 64
887 EPILOG_RESTORE_REG_PAIR x27, x28, 80
888 EPILOG_RESTORE_REG_PAIR_INDEXED fp, lr, 144
890 NESTED_END OnHijackTripThread, _TEXT
892 #endif // FEATURE_HIJACK
894 // ------------------------------------------------------------------
895 // Redirection Stub for GC in fully interruptible method
896 //GenerateRedirectedHandledJITCaseStub GCThreadControl
897 // ------------------------------------------------------------------
898 //GenerateRedirectedHandledJITCaseStub DbgThreadControl
899 // ------------------------------------------------------------------
900 //GenerateRedirectedHandledJITCaseStub UserSuspend
903 // ------------------------------------------------------------------
904 // Redirection Stub for GC Stress
905 GenerateRedirectedHandledJITCaseStub GCStress
909 // ------------------------------------------------------------------
911 // This helper enables us to call into a funclet after restoring Fp register
912 NESTED_ENTRY CallEHFunclet, _TEXT, NoHandler
917 // X2 = address of X19 register in CONTEXT record// used to restore the non-volatile registers of CrawlFrame
918 // X3 = address of the location where the SP of funclet's caller (i.e. this helper) should be saved.
921 // Using below prolog instead of PROLOG_SAVE_REG_PAIR_INDEXED fp,lr, -96
922 // is intentional. Above statement would also emit instruction to save
923 // sp in fp. If sp is saved in fp in prolog then it is not expected that fp can change in the body
924 // of method. However, this method needs to be able to change fp before calling funclet.
925 // This is required to access locals in funclet.
926 PROLOG_SAVE_REG_PAIR_INDEXED x29, lr, -96
928 // Spill callee saved registers
929 PROLOG_SAVE_REG_PAIR x19, x20, 16
930 PROLOG_SAVE_REG_PAIR x21, x22, 32
931 PROLOG_SAVE_REG_PAIR x23, x24, 48
932 PROLOG_SAVE_REG_PAIR x25, x26, 64
933 PROLOG_SAVE_REG_PAIR x27, x28, 80
935 // Save the SP of this function
939 ldp x19, x20, [x2, #0]
940 ldp x21, x22, [x2, #16]
941 ldp x23, x24, [x2, #32]
942 ldp x25, x26, [x2, #48]
943 ldp x27, x28, [x2, #64]
944 ldr fp, [x2, #80] // offset of fp in CONTEXT relative to X19
946 // Invoke the funclet
950 EPILOG_RESTORE_REG_PAIR x19, x20, 16
951 EPILOG_RESTORE_REG_PAIR x21, x22, 32
952 EPILOG_RESTORE_REG_PAIR x23, x24, 48
953 EPILOG_RESTORE_REG_PAIR x25, x26, 64
954 EPILOG_RESTORE_REG_PAIR x27, x28, 80
955 EPILOG_RESTORE_REG_PAIR_INDEXED fp, lr, 96
958 NESTED_END CallEHFunclet, _TEXT
960 // This helper enables us to call into a filter funclet by passing it the CallerSP to lookup the
961 // frame pointer for accessing the locals in the parent method.
962 NESTED_ENTRY CallEHFilterFunclet, _TEXT, NoHandler
964 PROLOG_SAVE_REG_PAIR_INDEXED fp, lr, -16
969 // X1 = SP of the caller of the method/funclet containing the filter
971 // X3 = address of the location where the SP of funclet's caller (i.e. this helper) should be saved.
973 // Save the SP of this function
975 // Invoke the filter funclet
978 EPILOG_RESTORE_REG_PAIR_INDEXED fp, lr, 16
981 NESTED_END CallEHFilterFunclet, _TEXT
983 #define FaultingExceptionFrame_StackAlloc (SIZEOF__GSCookie + SIZEOF__FaultingExceptionFrame)
984 #define FaultingExceptionFrame_FrameOffset SIZEOF__GSCookie
986 .macro GenerateRedirectedStubWithFrame stub, target
989 // This is the primary function to which execution will be redirected to.
991 NESTED_ENTRY \stub, _TEXT, NoHandler
994 // IN: lr: original IP before redirect
997 PROLOG_SAVE_REG_PAIR_INDEXED fp, lr, -16
998 PROLOG_STACK_ALLOC FaultingExceptionFrame_StackAlloc
1000 // At this point, the stack maybe misaligned if the thread abort was asynchronously
1001 // triggered in the prolog or epilog of the managed method. For such a case, we must
1002 // align the stack before calling into the VM.
1004 // Runtime check for 16-byte alignment.
1009 // Save pointer to FEF for GetFrameFromRedirectedStubStackFrame
1010 add x19, sp, #FaultingExceptionFrame_FrameOffset
1012 // Prepare to initialize to NULL
1014 str x1, [x19]// Initialize vtbl (it is not strictly necessary)
1015 str x1, [x19, #FaultingExceptionFrame__m_fFilterExecuted]// Initialize BOOL for personality routine
1017 mov x0, x19 // move the ptr to FEF in X0
1021 // Target should not return.
1024 NESTED_END \stub, _TEXT
1029 // ------------------------------------------------------------------
1030 // ResolveWorkerChainLookupAsmStub
1032 // This method will perform a quick chained lookup of the entry if the
1033 // initial cache lookup fails.
1036 // x9 contains the pointer to the current ResolveCacheElem
1037 // x11 contains the address of the indirection (and the flags in the low two bits)
1038 // x12 contains our contract the DispatchToken
1039 // Must be preserved:
1040 // x0 contains the instance object ref that we are making an interface call on
1041 // x9 Must point to a ResolveCacheElem [For Sanity]
1042 // [x1-x7] contains any additional register arguments for the interface method
1045 // x13 contains our type the MethodTable (from object ref in x0)
1048 // x0, [x1-x7] arguments for the interface implementation target
1050 // On Exit (to ResolveWorkerAsmStub):
1051 // x11 contains the address of the indirection and the flags in the low two bits.
1052 // x12 contains our contract (DispatchToken)
1053 // x16,x17 will be trashed
1056 #define BACKPATCH_FLAG 1
1057 #define PROMOTE_CHAIN_FLAG 2
1059 NESTED_ENTRY ResolveWorkerChainLookupAsmStub, _TEXT, NoHandler
1061 tst x11, #BACKPATCH_FLAG // First we check if x11 has the BACKPATCH_FLAG set
1062 bne LOCAL_LABEL(Fail) // If the BACKPATCH_FLAGS is set we will go directly to the ResolveWorkerAsmStub
1064 ldr x13, [x0] // retrieve the MethodTable from the object ref in x0
1065 LOCAL_LABEL(MainLoop):
1066 ldr x9, [x9, #ResolveCacheElem__pNext] // x9 <= the next entry in the chain
1068 beq LOCAL_LABEL(Fail)
1071 cmp x16, x13 // compare our MT with the one in the ResolveCacheElem
1072 bne LOCAL_LABEL(MainLoop)
1074 cmp x17, x12 // compare our DispatchToken with one in the ResolveCacheElem
1075 bne LOCAL_LABEL(MainLoop)
1077 LOCAL_LABEL(Success):
1078 PREPARE_EXTERNAL_VAR g_dispatch_cache_chain_success_counter, x13
1082 blt LOCAL_LABEL(Promote)
1084 ldr x16, [x9, #ResolveCacheElem__target] // get the ImplTarget
1085 br x16 // branch to interface implemenation target
1087 LOCAL_LABEL(Promote):
1088 // Move this entry to head postion of the chain
1090 str x16, [x13] // be quick to reset the counter so we don't get a bunch of contending threads
1091 orr x11, x11, #PROMOTE_CHAIN_FLAG // set PROMOTE_CHAIN_FLAG
1092 mov x12, x9 // We pass the ResolveCacheElem to ResolveWorkerAsmStub instead of the DispatchToken
1095 b ResolveWorkerAsmStub // call the ResolveWorkerAsmStub method to transition into the VM
1097 NESTED_END ResolveWorkerChainLookupAsmStub, _TEXT
1099 // ------------------------------------------------------------------
1100 // void ResolveWorkerAsmStub(args in regs x0-x7 & stack and possibly retbuf arg in x8, x11:IndirectionCellAndFlags, x12:DispatchToken)
1102 // The stub dispatch thunk which transfers control to VSD_ResolveWorker.
1103 NESTED_ENTRY ResolveWorkerAsmStub, _TEXT, NoHandler
1105 PROLOG_WITH_TRANSITION_BLOCK
1107 add x0, sp, #__PWTB_TransitionBlock // pTransitionBlock
1108 and x1, x11, #-4 // Indirection cell
1109 mov x2, x12 // DispatchToken
1110 and x3, x11, #3 // flag
1111 bl VSD_ResolveWorker
1114 EPILOG_WITH_TRANSITION_BLOCK_TAILCALL
1116 EPILOG_BRANCH_REG x9
1118 NESTED_END ResolveWorkerAsmStub, _TEXT
1120 #ifdef FEATURE_READYTORUN
1122 NESTED_ENTRY DelayLoad_MethodCall_FakeProlog, _TEXT, NoHandler
1123 DelayLoad_MethodCall:
1124 .global DelayLoad_MethodCall
1125 PROLOG_WITH_TRANSITION_BLOCK
1127 add x0, sp, #__PWTB_TransitionBlock // pTransitionBlock
1128 mov x1, x11 // Indirection cell
1129 mov x2, x9 // sectionIndex
1130 mov x3, x10 // Module*
1131 bl ExternalMethodFixupWorker
1134 EPILOG_WITH_TRANSITION_BLOCK_TAILCALL
1135 // Share patch label
1136 b ExternalMethodFixupPatchLabel
1138 NESTED_END DelayLoad_MethodCall_FakeProlog, _TEXT
1141 .macro DynamicHelper frameFlags, suffix
1142 NESTED_ENTRY DelayLoad_Helper\suffix\()_FakeProlog, _TEXT, NoHandler
1143 DelayLoad_Helper\suffix:
1144 .global DelayLoad_Helper\suffix
1146 PROLOG_WITH_TRANSITION_BLOCK
1148 add x0, sp, #__PWTB_TransitionBlock // pTransitionBlock
1149 mov x1, x11 // Indirection cell
1150 mov x2, x9 // sectionIndex
1151 mov x3, x10 // Module*
1153 bl DynamicHelperWorker
1154 cbnz x0, LOCAL_LABEL(FakeProlog\suffix\()_0)
1155 ldr x0, [sp, #__PWTB_ArgumentRegister_FirstArg]
1156 EPILOG_WITH_TRANSITION_BLOCK_RETURN
1157 LOCAL_LABEL(FakeProlog\suffix\()_0):
1159 EPILOG_WITH_TRANSITION_BLOCK_TAILCALL
1160 EPILOG_BRANCH_REG x12
1162 NESTED_END DelayLoad_Helper\suffix\()_FakeProlog, _TEXT
1165 DynamicHelper DynamicHelperFrameFlags_Default
1166 DynamicHelper DynamicHelperFrameFlags_ObjectArg, _Obj
1167 DynamicHelper DynamicHelperFrameFlags_ObjectArg | DynamicHelperFrameFlags_ObjectArg2, _ObjObj
1170 #ifdef FEATURE_PREJIT
1171 // ------------------------------------------------------------------
1172 // void StubDispatchFixupStub(args in regs x0-x7 & stack and possibly retbuff arg in x8, x11:IndirectionCellAndFlags)
1174 // The stub dispatch thunk which transfers control to StubDispatchFixupWorker.
1175 NESTED_ENTRY StubDispatchFixupStub, _TEXT, NoHandler
1177 PROLOG_WITH_TRANSITION_BLOCK
1179 add x0, sp, #__PWTB_TransitionBlock // pTransitionBlock
1180 and x1, x11, #-4 // Indirection cell
1181 mov x2, #0 // sectionIndex
1182 mov x3, #0 // pModule
1183 bl C_FUNC(StubDispatchFixupWorker)
1186 EPILOG_WITH_TRANSITION_BLOCK_TAILCALL
1187 PATCH_LABEL StubDispatchFixupPatchLabel
1188 EPILOG_BRANCH_REG x12
1190 NESTED_END StubDispatchFixupStub, _TEXT
1193 #ifdef FEATURE_COMINTEROP
1195 // Function used by COM interop to get floating point return value (since it's not in the same
1196 // register(s) as non-floating point values).
1199 // x0 : size of the FP result (4 or 8 bytes)
1200 // x1 : pointer to 64-bit buffer to receive result
1203 // buffer pointed to by x1 on entry contains the float or double argument as appropriate
1205 LEAF_ENTRY getFPReturn, _TEXT
1207 LEAF_END getFPReturn, _TEXT
1209 // ------------------------------------------------------------------
1210 // Function used by COM interop to set floating point return value (since it's not in the same
1211 // register(s) as non-floating point values).
1214 // x0 : size of the FP result (4 or 8 bytes)
1215 // x1 : 32-bit or 64-bit FP result
1218 // s0 : float result if x0 == 4
1219 // d0 : double result if x0 == 8
1221 LEAF_ENTRY setFPReturn, _TEXT
1223 LEAF_END setFPReturn, _TEXT
1227 // JIT Static access helpers when coreclr host specifies single appdomain flag
1230 // ------------------------------------------------------------------
1231 // void* JIT_GetSharedNonGCStaticBase(SIZE_T moduleDomainID, DWORD dwClassDomainID)
1233 LEAF_ENTRY JIT_GetSharedNonGCStaticBase_SingleAppDomain, _TEXT
1234 // If class is not initialized, bail to C++ helper
1235 add x2, x0, #DomainLocalModule__m_pDataBlob
1236 ldrb w2, [x2, w1, UXTW]
1238 beq LOCAL_LABEL(JIT_GetSharedNonGCStaticBase_SingleAppDomain_CallHelper)
1242 LOCAL_LABEL(JIT_GetSharedNonGCStaticBase_SingleAppDomain_CallHelper):
1243 // Tail call JIT_GetSharedNonGCStaticBase_Helper
1244 b C_FUNC(JIT_GetSharedNonGCStaticBase_Helper)
1245 LEAF_END JIT_GetSharedNonGCStaticBase_SingleAppDomain, _TEXT
1248 // ------------------------------------------------------------------
1249 // void* JIT_GetSharedNonGCStaticBaseNoCtor(SIZE_T moduleDomainID, DWORD dwClassDomainID)
1251 LEAF_ENTRY JIT_GetSharedNonGCStaticBaseNoCtor_SingleAppDomain, _TEXT
1253 LEAF_END JIT_GetSharedNonGCStaticBaseNoCtor_SingleAppDomain, _TEXT
1256 // ------------------------------------------------------------------
1257 // void* JIT_GetSharedGCStaticBase(SIZE_T moduleDomainID, DWORD dwClassDomainID)
1259 LEAF_ENTRY JIT_GetSharedGCStaticBase_SingleAppDomain, _TEXT
1260 // If class is not initialized, bail to C++ helper
1261 add x2, x0, #DomainLocalModule__m_pDataBlob
1262 ldrb w2, [x2, w1, UXTW]
1264 beq LOCAL_LABEL(JIT_GetSharedGCStaticBase_SingleAppDomain_CallHelper)
1266 ldr x0, [x0, #DomainLocalModule__m_pGCStatics]
1269 LOCAL_LABEL(JIT_GetSharedGCStaticBase_SingleAppDomain_CallHelper):
1270 // Tail call Jit_GetSharedGCStaticBase_Helper
1271 b C_FUNC(JIT_GetSharedGCStaticBase_Helper)
1272 LEAF_END JIT_GetSharedGCStaticBase_SingleAppDomain, _TEXT
1275 // ------------------------------------------------------------------
1276 // void* JIT_GetSharedGCStaticBaseNoCtor(SIZE_T moduleDomainID, DWORD dwClassDomainID)
1278 LEAF_ENTRY JIT_GetSharedGCStaticBaseNoCtor_SingleAppDomain, _TEXT
1279 ldr x0, [x0, #DomainLocalModule__m_pGCStatics]
1281 LEAF_END JIT_GetSharedGCStaticBaseNoCtor_SingleAppDomain, _TEXT
1283 // ------------------------------------------------------------------
1284 // __declspec(naked) void F_CALL_CONV JIT_Stelem_Ref(PtrArray* array, unsigned idx, Object* val)
1285 LEAF_ENTRY JIT_Stelem_Ref, _TEXT
1286 // We retain arguments as they were passed and use x0 == array x1 == idx x2 == val
1288 // check for null array
1289 cbz x0, LOCAL_LABEL(ThrowNullReferenceException)
1292 ldr x3,[x0,#ArrayBase__m_NumComponents]
1294 bls LOCAL_LABEL(ThrowIndexOutOfRangeException)
1296 // fast path to null assignment (doesn't need any write-barriers)
1297 cbz x2, LOCAL_LABEL(AssigningNull)
1299 // Verify the array-type and val-type matches before writing
1300 ldr x12, [x0] // x12 = array MT
1301 ldr x3, [x2] // x3 = val->GetMethodTable()
1302 ldr x12, [x12, #MethodTable__m_ElementType] // array->GetArrayElementTypeHandle()
1304 beq C_FUNC(JIT_Stelem_DoWrite)
1306 // Types didnt match but allow writing into an array of objects
1307 ldr x3, =g_pObjectClass
1308 ldr x3, [x3] // x3 = *g_pObjectClass
1309 cmp x3, x12 // array type matches with Object*
1310 beq C_FUNC(JIT_Stelem_DoWrite)
1312 // array type and val type do not exactly match. Raise frame and do detailed match
1313 b C_FUNC(JIT_Stelem_Ref_NotExactMatch)
1315 LOCAL_LABEL(AssigningNull):
1316 // Assigning null doesn't need write barrier
1317 add x0, x0, x1, LSL #3 // x0 = x0 + (x1 x 8) = array->m_array[idx]
1318 str x2, [x0, #PtrArray__m_Array] // array->m_array[idx] = val
1321 LOCAL_LABEL(ThrowNullReferenceException):
1322 // Tail call JIT_InternalThrow(NullReferenceException)
1323 ldr x0, =CORINFO_NullReferenceException_ASM
1324 b C_FUNC(JIT_InternalThrow)
1326 LOCAL_LABEL(ThrowIndexOutOfRangeException):
1327 // Tail call JIT_InternalThrow(NullReferenceException)
1328 ldr x0, =CORINFO_IndexOutOfRangeException_ASM
1329 b C_FUNC(JIT_InternalThrow)
1331 LEAF_END JIT_Stelem_Ref, _TEXT
1333 // ------------------------------------------------------------------
1334 // __declspec(naked) void F_CALL_CONV JIT_Stelem_Ref_NotExactMatch(PtrArray* array,
1335 // unsigned idx, Object* val)
1336 // x12 = array->GetArrayElementTypeHandle()
1338 NESTED_ENTRY JIT_Stelem_Ref_NotExactMatch, _TEXT, NoHandler
1339 PROLOG_SAVE_REG_PAIR_INDEXED fp, lr, -48
1340 // Spill callee saved registers
1341 PROLOG_SAVE_REG_PAIR x0, x1, 16
1342 PROLOG_SAVE_REG x2, 32
1344 // allow in case val can be casted to array element type
1345 // call ObjIsInstanceOfNoGC(val, array->GetArrayElementTypeHandle())
1346 mov x1, x12 // array->GetArrayElementTypeHandle()
1348 bl C_FUNC(ObjIsInstanceOfNoGC)
1349 cmp x0, TypeHandle_CanCast
1350 beq LOCAL_LABEL(DoWrite) // ObjIsInstance returned TypeHandle::CanCast
1352 // check via raising frame
1353 LOCAL_LABEL(NeedFrame):
1354 add x1, sp, #16 // x1 = &array
1355 add x0, sp, #32 // x0 = &val
1357 bl C_FUNC(ArrayStoreCheck) // ArrayStoreCheck(&val, &array)
1359 LOCAL_LABEL(DoWrite):
1360 EPILOG_RESTORE_REG_PAIR x0, x1, 16
1361 EPILOG_RESTORE_REG x2, 32
1362 EPILOG_RESTORE_REG_PAIR_INDEXED fp, lr, 48
1363 b C_FUNC(JIT_Stelem_DoWrite)
1364 NESTED_END JIT_Stelem_Ref_NotExactMatch, _TEXT
1366 // ------------------------------------------------------------------
1367 // __declspec(naked) void F_CALL_CONV JIT_Stelem_DoWrite(PtrArray* array, unsigned idx, Object* val)
1368 LEAF_ENTRY JIT_Stelem_DoWrite, _TEXT
1370 // Setup args for JIT_WriteBarrier. x14 = &array->m_array[idx] x15 = val
1371 add x14, x0, #PtrArray__m_Array // x14 = &array->m_array
1372 add x14, x14, x1, LSL #3
1373 mov x15, x2 // x15 = val
1375 // Branch to the write barrier (which is already correctly overwritten with
1376 // single or multi-proc code based on the current CPU
1377 b C_FUNC(JIT_WriteBarrier)
1378 LEAF_END JIT_Stelem_DoWrite, _TEXT