Merge pull request #21795 from fiigii/fixGeneric
[platform/upstream/coreclr.git] / src / vm / arm64 / asmhelpers.S
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.
4
5 #include "asmconstants.h"
6 #include "unixasmmacros.inc"
7
8 // LPVOID __stdcall GetCurrentIP(void)//
9 LEAF_ENTRY GetCurrentIP, _TEXT
10     mov x0, lr
11     ret lr
12 LEAF_END GetCurrentIP, _TEXT
13
14 // LPVOID __stdcall GetCurrentSP(void)//
15 LEAF_ENTRY GetCurrentSP, _TEXT
16     mov x0, sp
17     ret lr
18 LEAF_END GetCurrentSP, _TEXT
19
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
26     mov w1, #0
27     str w1, [x0, #MachState__isValid]
28
29     str lr, [x0, #LazyMachState_captureIp]
30
31     // str instruction does not save sp register directly so move to temp register
32     mov x1, sp
33     str x1, [x0, #LazyMachState_captureSp]
34
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)]
43
44     ret lr
45 LEAF_END LazyMachStateCaptureState, _TEXT
46
47 //
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.
53 //
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
57 // frame.
58 //
59 // So, if ptrX19_X29 points into the MachState, we need to update
60 // the register here.  That's what this macro does.
61 //
62 .macro RestoreRegMS regIndex, reg
63     // Incoming:
64     //
65     // x0 = address of MachState
66     //
67     // $regIndex: Index of the register (x19-x28). For x19, index is 19.
68     //For x20, index is 20, and so on.
69     //
70     // $reg: Register name (e.g. x19, x20, etc)
71     //
72     // Get the address of the specified captured register from machine state
73     add x2, x0, #(MachState__captureX19_X29 + ((\regIndex-19)*8))
74
75     // Get the content of specified preserved register pointer from machine state
76     ldr x3, [x0, #(MachState__ptrX19_X29 + ((\regIndex-19)*8))]
77
78     cmp x2, x3
79     bne LOCAL_LABEL(NoRestore_\reg)
80     ldr \reg, [x2]
81 LOCAL_LABEL(NoRestore_\reg):
82
83 .endmacro
84
85 // EXTERN_C int __fastcall HelperMethodFrameRestoreState(
86 // INDEBUG_COMMA(HelperMethodFrame *pFrame)
87 // MachState *pState
88 // )
89 LEAF_ENTRY HelperMethodFrameRestoreState, _TEXT
90
91     #ifdef _DEBUG
92     mov x0, x1
93     #endif
94
95     // If machine state is invalid, then simply exit
96     ldr w1, [x0, #MachState__isValid]
97     cmp w1, #0
98     beq LOCAL_LABEL(Done)
99
100     RestoreRegMS 19, X19
101     RestoreRegMS 20, X20
102     RestoreRegMS 21, X21
103     RestoreRegMS 22, X22
104     RestoreRegMS 23, X23
105     RestoreRegMS 24, X24
106     RestoreRegMS 25, X25
107     RestoreRegMS 26, X26
108     RestoreRegMS 27, X27
109     RestoreRegMS 28, X28
110     RestoreRegMS 29, X29
111 LOCAL_LABEL(Done):
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.
115     mov x0,#0
116     ret lr
117
118 LEAF_END HelperMethodFrameRestoreState, _TEXT
119
120 // ------------------------------------------------------------------
121 // The call in ndirect import precode points to this function.
122 NESTED_ENTRY NDirectImportThunk, _TEXT, NoHandler
123
124     PROLOG_SAVE_REG_PAIR_INDEXED fp, lr, -160
125     SAVE_ARGUMENT_REGISTERS sp, 16
126     SAVE_FLOAT_ARGUMENT_REGISTERS sp, 88
127
128     mov x0, x12
129     bl NDirectImportWorker
130     mov x12, x0
131
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
136
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
140
141 NESTED_END NDirectImportThunk, _TEXT
142
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 *
148     // On Exit
149     // x12 = MethodDesc*
150     // x13, x14 Trashed
151     // Inline computation done by FixupPrecode::GetMethodDesc()
152     ldrb    w13, [x12, #Offset_PrecodeChunkIndex]    //m_PrecodeChunkIndex
153     ldrb    w14, [x12, #Offset_MethodDescChunkIndex] // m_MethodDescChunkIndex
154
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
159
160     b ThePreStub
161 NESTED_END PrecodeFixupThunk, _TEXT
162 // ------------------------------------------------------------------
163
164 NESTED_ENTRY ThePreStub, _TEXT, NoHandler
165
166     PROLOG_WITH_TRANSITION_BLOCK
167
168     add x0, sp, #__PWTB_TransitionBlock // pTransitionBlock
169     mov x1, METHODDESC_REGISTER // pMethodDesc
170
171     bl  PreStubWorker
172
173     mov x9, x0
174
175     EPILOG_WITH_TRANSITION_BLOCK_TAILCALL
176     EPILOG_BRANCH_REG  x9
177
178 NESTED_END ThePreStub, _TEXT
179
180 // ------------------------------------------------------------------
181 // ThePreStubPatch()
182
183 LEAF_ENTRY ThePreStubPatch, _TEXT
184     nop
185 .globl C_FUNC(ThePreStubPatchLabel)
186 C_FUNC(ThePreStubPatchLabel):
187     ret lr
188 LEAF_END ThePreStubPatch, _TEXT
189
190
191 //-----------------------------------------------------------------------------
192 // The following Macros help in WRITE_BARRIER Implemetations
193 // WRITE_BARRIER_ENTRY
194 //
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.
197 //
198 .macro WRITE_BARRIER_ENTRY name
199     LEAF_ENTRY \name, _TEXT
200 .endmacro
201
202 // WRITE_BARRIER_END
203 //
204 // The partner to WRITE_BARRIER_ENTRY, used like NESTED_END.
205 //
206 .macro WRITE_BARRIER_END name
207     LEAF_END_MARKED \name, _TEXT
208 .endmacro
209
210 // ------------------------------------------------------------------
211 // Start of the writeable code region
212 LEAF_ENTRY JIT_PatchedCodeStart, _TEXT
213     ret  lr
214 LEAF_END JIT_PatchedCodeStart, _TEXT
215
216 // void JIT_UpdateWriteBarrierState(bool skipEphemeralCheck)
217 //
218 // Update shadow copies of the various state info required for barrier
219 //
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.
223 //
224 // Align and group state info together so it fits in a single cache line
225 // and each entry can be written atomically
226 //
227 WRITE_BARRIER_ENTRY JIT_UpdateWriteBarrierState
228     PROLOG_SAVE_REG_PAIR_INDEXED   fp, lr, -16
229
230     // x0-x7 will contain intended new state
231     // x8 will preserve skipEphemeralCheck
232     // x12 will be used for pointers
233
234     mov x8, x0
235
236     PREPARE_EXTERNAL_VAR g_card_table, x12
237     ldr  x0, [x12]
238
239 #ifdef FEATURE_MANUALLY_MANAGED_CARD_BUNDLES
240     PREPARE_EXTERNAL_VAR g_card_bundle_table, x12
241     ldr  x1, [x12]
242 #endif
243
244 #ifdef WRITE_BARRIER_CHECK
245     PREPARE_EXTERNAL_VAR g_GCShadow, x12
246     ldr  x2, [x12]
247 #endif
248
249 #ifdef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
250     PREPARE_EXTERNAL_VAR g_sw_ww_table, x12
251     ldr  x3, [x12]
252 #endif
253
254     PREPARE_EXTERNAL_VAR g_ephemeral_low, x12
255     ldr  x4, [x12]
256
257     PREPARE_EXTERNAL_VAR g_ephemeral_high, x12
258     ldr  x5, [x12]
259
260     cbz  x8, LOCAL_LABEL(EphemeralCheckEnabled)
261     movz x4, #0
262     movn x5, #0
263 LOCAL_LABEL(EphemeralCheckEnabled):
264
265     PREPARE_EXTERNAL_VAR g_lowest_address, x12
266     ldr  x6, [x12]
267
268     PREPARE_EXTERNAL_VAR g_highest_address, x12
269     ldr  x7, [x12]
270
271     // Update wbs state
272     adr  x12, LOCAL_LABEL(wbs_begin)
273
274     stp  x0, x1, [x12], 16
275     stp  x2, x3, [x12], 16
276     stp  x4, x5, [x12], 16
277     stp  x6, x7, [x12], 16
278
279     EPILOG_RESTORE_REG_PAIR_INDEXED fp, lr, 16
280     EPILOG_RETURN
281
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):
286     .quad 0
287 LOCAL_LABEL(wbs_card_bundle_table):
288     .quad 0
289 LOCAL_LABEL(wbs_GCShadow):
290     .quad 0
291 LOCAL_LABEL(wbs_sw_ww_table):
292     .quad 0
293 LOCAL_LABEL(wbs_ephemeral_low):
294     .quad 0
295 LOCAL_LABEL(wbs_ephemeral_high):
296     .quad 0
297 LOCAL_LABEL(wbs_lowest_address):
298     .quad 0
299 LOCAL_LABEL(wbs_highest_address):
300     .quad 0
301 WRITE_BARRIER_END JIT_UpdateWriteBarrierState
302
303
304 // ------------------------------------------------------------------
305 // End of the writeable code region
306 LEAF_ENTRY JIT_PatchedCodeLast, _TEXT
307     ret  lr
308 LEAF_END JIT_PatchedCodeLast, _TEXT
309
310 // void JIT_ByRefWriteBarrier
311 // On entry:
312 //   x13  : the source address (points to object reference to write)
313 //   x14  : the destination address (object reference written here)
314 //
315 // On exit:
316 //   x12  : trashed
317 //   x13  : incremented by 8
318 //   x14  : incremented by 8
319 //   x15  : trashed
320 //   x17  : trashed (ip1) if FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
321 //
322 WRITE_BARRIER_ENTRY JIT_ByRefWriteBarrier
323
324     ldr  x15, [x13], 8
325     b C_FUNC(JIT_CheckedWriteBarrier)
326
327 WRITE_BARRIER_END JIT_ByRefWriteBarrier 
328
329 //-----------------------------------------------------------------------------
330 // Simple WriteBarriers
331 // void JIT_CheckedWriteBarrier(Object** dst, Object* src)
332 // On entry:
333 //   x14  : the destination address (LHS of the assignment)
334 //   x15  : the object reference (RHS of the assignment)
335 //
336 // On exit:
337 //   x12  : trashed
338 //   x14  : trashed (incremented by 8 to implement JIT_ByRefWriteBarrier contract)
339 //   x15  : trashed
340 //   x17  : trashed (ip1) if FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
341 //
342 WRITE_BARRIER_ENTRY JIT_CheckedWriteBarrier
343     ldr  x12,  LOCAL_LABEL(wbs_lowest_address)
344     cmp  x14,  x12
345
346     ldr  x12, LOCAL_LABEL(wbs_highest_address)
347
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
353
354     blo  C_FUNC(JIT_WriteBarrier)
355
356 LOCAL_LABEL(NotInHeap):
357     str  x15, [x14], 8
358     ret  lr
359 WRITE_BARRIER_END JIT_CheckedWriteBarrier
360
361 // void JIT_WriteBarrier(Object** dst, Object* src)
362 // On entry:
363 //   x14  : the destination address (LHS of the assignment)
364 //   x15  : the object reference (RHS of the assignment)
365 //
366 // On exit:
367 //   x12  : trashed
368 //   x14  : trashed (incremented by 8 to implement JIT_ByRefWriteBarrier contract)
369 //   x15  : trashed
370 //   x17  : trashed (ip1) if FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
371 //
372 WRITE_BARRIER_ENTRY JIT_WriteBarrier
373     stlr  x15, [x14]
374
375 #ifdef WRITE_BARRIER_CHECK
376     // Update GC Shadow Heap
377
378     // Do not perform the work if g_GCShadow is 0
379     ldr  x12, LOCAL_LABEL(wbs_GCShadow)
380     cbz  x12, LOCAL_LABEL(ShadowUpdateDisabled)
381
382     // need temporary register. Save before using.
383     str  x13, [sp, #-16]!
384
385     // Compute address of shadow heap location:
386     //   pShadow = g_GCShadow + (x14 - g_lowest_address)
387     ldr  x13, LOCAL_LABEL(wbs_lowest_address)
388     sub  x13, x14, x13
389     add  x12, x13, x12
390
391     // if (pShadow >= g_GCShadowEnd) goto end
392     PREPARE_EXTERNAL_VAR g_GCShadowEnd, x13
393     ldr  x13, [x13]
394     cmp  x12, x13
395     bhs  LOCAL_LABEL(ShadowUpdateEnd)
396
397     // *pShadow = x15
398     str  x15, [x12]
399
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.
402     dmb  ish
403
404     // if ([x14] == x15) goto end
405     ldr  x13, [x14]
406     cmp  x13, x15
407     beq LOCAL_LABEL(ShadowUpdateEnd)
408
409     // *pShadow = INVALIDGCVALUE (0xcccccccd)
410     movz x13, #0xcccd
411     movk x13, #0xcccc, LSL #16
412     str  x13, [x12]
413
414 LOCAL_LABEL(ShadowUpdateEnd):
415     ldr  x13, [sp], #16
416 LOCAL_LABEL(ShadowUpdateDisabled):
417 #endif
418
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
424     ldrb w17, [x12]
425     cbnz x17, LOCAL_LABEL(CheckCardTable)
426     mov  w17, #0xFF
427     strb w17, [x12]
428 #endif
429
430 LOCAL_LABEL(CheckCardTable):
431     // Branch to Exit if the reference is not in the Gen0 heap
432     //
433     ldr  x12, LOCAL_LABEL(wbs_ephemeral_low)
434     cbz  x12, LOCAL_LABEL(SkipEphemeralCheck)
435     cmp  x15,  x12
436
437     ldr  x12, LOCAL_LABEL(wbs_ephemeral_high)
438
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
444
445     bhi  LOCAL_LABEL(Exit)
446
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
451     ldrb w12, [x15]
452     cmp  x12, 0xFF
453     beq  LOCAL_LABEL(Exit)
454
455 LOCAL_LABEL(UpdateCardTable):
456     mov  x12, 0xFF 
457     strb w12, [x15]
458
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
463     ldrb w12, [x15]
464     cmp  x12, 0xFF
465     beq  LOCAL_LABEL(Exit)
466
467 LOCAL_LABEL(UpdateCardBundle):
468     mov  x12, 0xFF
469     strb w12, [x15]
470 #endif
471
472 LOCAL_LABEL(Exit):
473     add  x14, x14, 8
474     ret  lr  
475 WRITE_BARRIER_END JIT_WriteBarrier
476
477 //------------------------------------------------
478 // VirtualMethodFixupStub
479 //
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.
485 //
486 // This is done lazily for performance reasons.
487 //
488 // On entry:
489 //
490 // x0 = "this" pointer
491 // x12 = Address of thunk
492
493 NESTED_ENTRY VirtualMethodFixupStub, _TEXT, NoHandler
494
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
499
500     // Refer to ZapImportVirtualThunk::Save
501     // for details on this.
502     //
503     // Move the thunk start address in x1
504     mov x1, x12
505
506     // Call the helper in the VM to perform the actual fixup
507     // and tell us where to tail call. x0 already contains
508     // the this pointer.
509     bl C_FUNC(VirtualMethodFixupWorker)
510     // On return, x0 contains the target to tailcall to
511     mov x12, x0
512
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
517
518     PATCH_LABEL VirtualMethodFixupPatchLabel
519
520     // and tailcall to the actual method
521     EPILOG_BRANCH_REG x12
522
523 NESTED_END VirtualMEthodFixupStub, _TEXT
524
525 //------------------------------------------------
526 // ExternalMethodFixupStub
527 //
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.
533 //
534 // This is done lazily for performance reasons.
535 //
536 // On entry:
537 //
538 // x12 = Address of thunk 
539
540 NESTED_ENTRY ExternalMethodFixupStub, _TEXT, NoHandler
541
542     PROLOG_WITH_TRANSITION_BLOCK
543
544     add x0, sp, #__PWTB_TransitionBlock // pTransitionBlock
545     mov x1, x12 // pThunk
546
547     bl C_FUNC(ExternalMethodFixupWorker)
548
549     // mov the address we patched to in x12 so that we can tail call to it
550     mov x12, x0
551
552     EPILOG_WITH_TRANSITION_BLOCK_TAILCALL
553     PATCH_LABEL ExternalMethodFixupPatchLabel
554     EPILOG_BRANCH_REG   x12
555
556 NESTED_END ExternalMethodFixupStub, _TEXT
557
558 // void SinglecastDelegateInvokeStub(Delegate *pThis)
559 LEAF_ENTRY SinglecastDelegateInvokeStub, _TEXT
560     cmp x0, #0
561     beq LOCAL_LABEL(LNullThis)
562
563     ldr x16, [x0, #DelegateObject___methodPtr]
564     ldr x0, [x0, #DelegateObject___target]
565
566     br x16
567
568 LOCAL_LABEL(LNullThis):
569     mov x0, #CORINFO_NullReferenceException_ASM
570     b C_FUNC(JIT_InternalThrow)
571
572 LEAF_END SinglecastDelegateInvokeStub, _TEXT
573
574 #ifdef FEATURE_COMINTEROP
575
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))
581
582 #define ComCallPreStub_FrameOffset   (ComCallPreStub_StackAlloc - (SIZEOF__ComMethodFrame - ComCallPreStub_FirstStackAdjust))
583 #define ComCallPreStub_ErrorReturnOffset0 SIZEOF__FloatArgumentRegisters
584
585 #define ComCallPreStub_FirstStackAdjust (ComCallPreStub_ErrorReturnOffset0 + (ComCallPreStub_ErrorReturnOffset0 & 8))
586
587 // ------------------------------------------------------------------
588 // COM to CLR stub called the first time a particular method is invoked.//
589 //
590 // On entry:
591 //   x12         : ComCallMethodDesc* provided by prepad thunk
592 //   plus user arguments in registers and on the stack
593 //
594 // On exit:
595 //   tail calls to real method
596 //
597 NESTED_ENTRY ComCallPreStub, _TEXT, NoHandler
598
599     // Save arguments and return address
600     PROLOG_SAVE_REG_PAIR           fp, lr, -ComCallPreStub_FirstStackAdjust!
601     PROLOG_STACK_ALLOC  ComCallPreStub_StackAlloc
602
603     SAVE_ARGUMENT_REGISTERS        sp, (16+ComCallPreStub_StackAlloc)
604
605     SAVE_FLOAT_ARGUMENT_REGISTERS  sp, 0
606
607     str x12, [sp, #(ComCallPreStub_FrameOffset + UnmanagedToManagedFrame__m_pvDatum)]
608     add x0, sp, #(ComCallPreStub_FrameOffset)
609     add x1, sp, #(ComCallPreStub_ErrorReturnOffset)
610     bl ComPreStubWorker
611
612     cbz x0, ComCallPreStub_ErrorExit
613
614     mov x12, x0
615
616     // pop the stack and restore original register state
617     RESTORE_FLOAT_ARGUMENT_REGISTERS  sp, 0
618     RESTORE_ARGUMENT_REGISTERS        sp, (16+ComCallPreStub_StackAlloc)
619
620     EPILOG_STACK_FREE ComCallPreStub_StackAlloc
621     EPILOG_RESTORE_REG_PAIR           fp, lr, ComCallPreStub_FirstStackAdjust!
622
623     // and tailcall to the actual method
624     EPILOG_BRANCH_REG x12
625
626 ComCallPreStub_ErrorExit
627     ldr x0, [sp, #(ComCallPreStub_ErrorReturnOffset)] // ErrorReturn
628
629     // pop the stack
630     EPILOG_STACK_FREE ComCallPreStub_StackAlloc
631     EPILOG_RESTORE_REG_PAIR           fp, lr, ComCallPreStub_FirstStackAdjust!
632
633     EPILOG_RETURN
634
635 NESTED_END ComCallPreStub, _TEXT
636
637 // ------------------------------------------------------------------
638 // COM to CLR stub which sets up a ComMethodFrame and calls COMToCLRWorker.
639 //
640 // On entry:
641 //   x12         : ComCallMethodDesc*  provided by prepad thunk
642 //   plus user arguments in registers and on the stack
643 //
644 // On exit:
645 //   Result in x0/d0 as per the real method being called
646 //
647     NESTED_ENTRY GenericComCallStub, _TEXT, NoHandler
648
649     // Save arguments and return address
650     PROLOG_SAVE_REG_PAIR           fp, lr, -GenericComCallStub_FirstStackAdjust!
651     PROLOG_STACK_ALLOC  GenericComCallStub_StackAlloc
652
653     SAVE_ARGUMENT_REGISTERS        sp, (16+GenericComCallStub_StackAlloc)
654     SAVE_FLOAT_ARGUMENT_REGISTERS  sp, 0
655
656     str x12, [sp, #(GenericComCallStub_FrameOffset + UnmanagedToManagedFrame__m_pvDatum)]
657     add x1, sp, #GenericComCallStub_FrameOffset
658     bl COMToCLRWorker
659
660     // pop the stack
661     EPILOG_STACK_FREE GenericComCallStub_StackAlloc
662     EPILOG_RESTORE_REG_PAIR           fp, lr, GenericComCallStub_FirstStackAdjust!
663
664     EPILOG_RETURN
665
666     NESTED_END GenericComCallStub, _TEXT
667
668 // ------------------------------------------------------------------
669 // COM to CLR stub called from COMToCLRWorker that actually dispatches to the real managed method.
670 //
671 // On entry:
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
677 //
678 // On exit:
679 //   Result in x0/d0 as per the real method being called
680 //
681     NESTED_ENTRY COMToCLRDispatchHelper, _TEXT,CallDescrWorkerUnwindFrameChainHandler
682
683     PROLOG_SAVE_REG_PAIR           fp, lr, -16!
684
685     cbz x0, COMToCLRDispatchHelper_RegSetup
686
687     add x9, x1, #SIZEOF__ComMethodFrame
688     add x9, x9, x0, LSL #3
689 COMToCLRDispatchHelper_StackLoop
690     ldr x8, [x9, #-8]!
691     str x8, [sp, #-8]!
692     sub x0, x0, #1
693     cbnz x0, COMToCLRDispatchHelper_StackLoop
694
695 COMToCLRDispatchHelper_RegSetup
696
697     RESTORE_FLOAT_ARGUMENT_REGISTERS x1, -1 * GenericComCallStub_FrameOffset
698
699     mov lr, x2
700     mov x12, x3
701
702     mov x0, x4
703
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)]
708
709     ldr x1, [x1, #(SIZEOF__ComMethodFrame - SIZEOF__ArgumentRegisters + 8)]
710
711     blr lr
712
713     EPILOG_STACK_RESTORE
714     EPILOG_RESTORE_REG_PAIR           fp, lr, 16!
715     EPILOG_RETURN
716
717     NESTED_END COMToCLRDispatchHelper, _TEXT
718
719 #endif // FEATURE_COMINTEROP
720 //
721 // x12 = UMEntryThunk*
722 //
723 NESTED_ENTRY TheUMEntryPrestub, _TEXT, UnhandledExceptionHandlerUnix
724
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
729
730     mov x0, x12
731     bl C_FUNC(TheUMEntryPrestubWorker)
732
733     // save real target address in x12.
734     mov x12, x0
735
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
740
741     // and tailcall to the actual method
742     EPILOG_BRANCH_REG x12
743
744 NESTED_END TheUMEntryPrestub, _TEXT
745
746 //
747 // x12 = UMEntryThunk*
748 //
749 NESTED_ENTRY UMThunkStub, _TEXT, UnhandledExceptionHandlerUnix
750
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
755
756     SAVE_ARGUMENT_REGISTERS sp, 16
757
758 #define UMThunkStub_HiddenArg 88 // offset of saved UMEntryThunk *
759 #define UMThunkStub_StackArgs 112 // offset of original stack args (total size of UMThunkStub frame)
760
761     // save UMEntryThunk*
762     str x12, [sp, #UMThunkStub_HiddenArg]
763
764     // assuming GetThread does not clobber FP Args
765     bl C_FUNC(GetThread)
766     cbz x0, LOCAL_LABEL(UMThunkStub_DoThreadSetup)
767
768 LOCAL_LABEL(UMThunkStub_HaveThread):
769     mov x19, x0  // x19 = Thread *
770
771     mov x9, 1
772     // m_fPreemptiveGCDisabled is 4 byte field so using 32-bit variant
773     str w9, [x19, #Thread__m_fPreemptiveGCDisabled]
774
775     PREPARE_EXTERNAL_VAR g_TrapReturningThreads, x2
776     ldr x3, [x2]
777     // assuming x0 contains Thread* before jumping to UMThunkStub_DoTrapReturningThreads
778     cbnz x3, LOCAL_LABEL(UMThunkStub_DoTrapReturningThreads)
779
780 LOCAL_LABEL(UMThunkStub_InCooperativeMode):
781     ldr x12, [fp, #UMThunkStub_HiddenArg] // x12 = UMEntryThunk*
782     ldr x3, [x12, #UMEntryThunk__m_pUMThunkMarshInfo] // x3 = m_pUMThunkMarshInfo
783
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)
787
788     // extend to 64-bits
789     uxtw x2, w2
790
791     // Source pointer
792     add x0, fp, #UMThunkStub_StackArgs
793
794     // move source pointer to end of Stack Args
795     add x0, x0, x2 
796
797     // Count of stack slot pairs to copy (divide by 16)
798     lsr x1, x2, #4
799
800     // Is there an extra stack slot (can happen when stack arg bytes not multiple of 16)
801     and x2, x2, #8
802
803     // If yes then start source pointer from 16 byte aligned stack slot
804     add x0, x0, x2  
805
806     // increment stack slot pair count by 1 if x2 is not zero
807     add x1, x1, x2, LSR #3 
808
809 LOCAL_LABEL(UMThunkStub_StackLoop):
810     ldp x4, x5, [x0, #-16]! // pre-Index
811     stp x4, x5, [sp, #-16]! // pre-Index
812     subs x1, x1, #1
813     bne LOCAL_LABEL(UMThunkStub_StackLoop)
814
815 LOCAL_LABEL(UMThunkStub_RegArgumentsSetup):
816     ldr x16, [x3, #UMThunkMarshInfo__m_pILStub]
817
818     RESTORE_ARGUMENT_REGISTERS fp, 16
819
820     blr x16
821
822 LOCAL_LABEL(UMThunkStub_PostCall):
823     mov x4, 0
824     // m_fPreemptiveGCDisabled is 4 byte field so using 32-bit variant
825     str w4, [x19, #Thread__m_fPreemptiveGCDisabled]
826
827     EPILOG_STACK_RESTORE
828     EPILOG_RESTORE_REG x19, 96
829     EPILOG_RESTORE_REG_PAIR_INDEXED   fp, lr, 112
830
831     EPILOG_RETURN
832
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)
840
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)
851
852 NESTED_END UMThunkStub, _TEXT
853
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
865
866     // save any integral return value(s)
867     stp x0, x1, [sp, #96]
868
869     // save any FP/HFA return value(s)
870     stp d0, d1, [sp, #112]
871     stp d2, d3, [sp, #128]
872
873     mov x0, sp
874     bl OnHijackWorker
875
876     // restore any integral return value(s)
877     ldp x0, x1, [sp, #96]
878
879     // restore any FP/HFA return value(s)
880     ldp d0, d1, [sp, #112]
881     ldp d2, d3, [sp, #128]
882
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
889     EPILOG_RETURN
890 NESTED_END OnHijackTripThread, _TEXT
891
892 #endif // FEATURE_HIJACK
893
894 // ------------------------------------------------------------------
895 // Redirection Stub for GC in fully interruptible method
896 //GenerateRedirectedHandledJITCaseStub GCThreadControl
897 // ------------------------------------------------------------------
898 //GenerateRedirectedHandledJITCaseStub DbgThreadControl
899 // ------------------------------------------------------------------
900 //GenerateRedirectedHandledJITCaseStub UserSuspend
901
902 #ifdef _DEBUG
903 // ------------------------------------------------------------------
904 // Redirection Stub for GC Stress
905 GenerateRedirectedHandledJITCaseStub GCStress
906 #endif
907
908
909 // ------------------------------------------------------------------
910
911 // This helper enables us to call into a funclet after restoring Fp register
912 NESTED_ENTRY CallEHFunclet, _TEXT, NoHandler
913     // On entry:
914     //
915     // X0 = throwable
916     // X1 = PC to invoke
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.
919     //
920
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
927
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
934
935     // Save the SP of this function
936     mov x4, sp
937     str x4, [x3]
938
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
945
946     // Invoke the funclet
947     blr x1
948     nop
949
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
956     EPILOG_RETURN
957
958 NESTED_END CallEHFunclet, _TEXT
959
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
963
964     PROLOG_SAVE_REG_PAIR_INDEXED   fp, lr, -16
965
966     // On entry:
967     //
968     // X0 = throwable
969     // X1 = SP of the caller of the method/funclet containing the filter
970     // X2 = PC to invoke
971     // X3 = address of the location where the SP of funclet's caller (i.e. this helper) should be saved.
972     //
973     // Save the SP of this function
974     str fp, [x3]
975     // Invoke the filter funclet
976     blr x2
977
978     EPILOG_RESTORE_REG_PAIR_INDEXED   fp, lr,   16
979     EPILOG_RETURN
980
981 NESTED_END CallEHFilterFunclet, _TEXT
982
983 #define FaultingExceptionFrame_StackAlloc (SIZEOF__GSCookie + SIZEOF__FaultingExceptionFrame)
984 #define FaultingExceptionFrame_FrameOffset SIZEOF__GSCookie
985
986 .macro GenerateRedirectedStubWithFrame stub, target
987
988     // 
989     // This is the primary function to which execution will be redirected to.
990     //
991     NESTED_ENTRY \stub, _TEXT, NoHandler
992
993         //
994         // IN: lr: original IP before redirect
995         //
996
997         PROLOG_SAVE_REG_PAIR_INDEXED fp, lr, -16
998         PROLOG_STACK_ALLOC  FaultingExceptionFrame_StackAlloc
999
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.
1003         //
1004         // Runtime check for 16-byte alignment. 
1005         mov x0, sp
1006         and x0, x0, #15
1007         sub sp, sp, x0
1008
1009         // Save pointer to FEF for GetFrameFromRedirectedStubStackFrame
1010         add x19, sp, #FaultingExceptionFrame_FrameOffset
1011
1012         // Prepare to initialize to NULL
1013         mov x1,#0
1014         str x1, [x19]// Initialize vtbl (it is not strictly necessary)
1015         str x1, [x19, #FaultingExceptionFrame__m_fFilterExecuted]// Initialize BOOL for personality routine
1016
1017         mov x0, x19   // move the ptr to FEF in X0
1018
1019         bl C_FUNC(\target)
1020
1021         // Target should not return.
1022         EMIT_BREAKPOINT
1023
1024     NESTED_END \stub, _TEXT
1025
1026 .endmacro
1027
1028
1029 // ------------------------------------------------------------------
1030 // ResolveWorkerChainLookupAsmStub
1031 //
1032 // This method will perform a quick chained lookup of the entry if the
1033 //  initial cache lookup fails.
1034 //
1035 // On Entry:
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
1043 //
1044 // Loaded from x0
1045 //   x13       contains our type     the MethodTable  (from object ref in x0)
1046 //
1047 // On Exit:
1048 //   x0, [x1-x7] arguments for the interface implementation target
1049 //
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
1054 //
1055
1056 #define BACKPATCH_FLAG      1
1057 #define PROMOTE_CHAIN_FLAG  2
1058
1059 NESTED_ENTRY ResolveWorkerChainLookupAsmStub, _TEXT, NoHandler
1060
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
1063
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
1067     cmp     x9, #0
1068     beq     LOCAL_LABEL(Fail)
1069
1070     ldp     x16, x17, [x9]
1071     cmp     x16, x13          // compare our MT with the one in the ResolveCacheElem
1072     bne     LOCAL_LABEL(MainLoop)
1073
1074     cmp     x17, x12          // compare our DispatchToken with one in the ResolveCacheElem
1075     bne     LOCAL_LABEL(MainLoop)
1076
1077 LOCAL_LABEL(Success):
1078     PREPARE_EXTERNAL_VAR g_dispatch_cache_chain_success_counter, x13
1079     ldr     x16, [x13]
1080     subs    x16, x16, #1
1081     str     x16, [x13]
1082     blt     LOCAL_LABEL(Promote)
1083
1084     ldr     x16, [x9, #ResolveCacheElem__target]    // get the ImplTarget
1085     br      x16               // branch to interface implemenation target
1086
1087 LOCAL_LABEL(Promote):
1088                               // Move this entry to head postion of the chain
1089     mov     x16, #256
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
1093
1094 LOCAL_LABEL(Fail):
1095     b       ResolveWorkerAsmStub // call the ResolveWorkerAsmStub method to transition into the VM
1096
1097 NESTED_END ResolveWorkerChainLookupAsmStub, _TEXT
1098
1099 // ------------------------------------------------------------------
1100 // void ResolveWorkerAsmStub(args in regs x0-x7 & stack and possibly retbuf arg in x8, x11:IndirectionCellAndFlags, x12:DispatchToken)
1101 //
1102 // The stub dispatch thunk which transfers control to VSD_ResolveWorker.
1103 NESTED_ENTRY ResolveWorkerAsmStub, _TEXT, NoHandler
1104
1105     PROLOG_WITH_TRANSITION_BLOCK
1106
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
1112     mov x9, x0
1113
1114     EPILOG_WITH_TRANSITION_BLOCK_TAILCALL
1115
1116     EPILOG_BRANCH_REG  x9
1117
1118 NESTED_END ResolveWorkerAsmStub, _TEXT
1119
1120 #ifdef FEATURE_READYTORUN
1121
1122 NESTED_ENTRY DelayLoad_MethodCall_FakeProlog, _TEXT, NoHandler
1123 DelayLoad_MethodCall:
1124     .global DelayLoad_MethodCall
1125     PROLOG_WITH_TRANSITION_BLOCK
1126
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
1132     mov x12, x0
1133
1134     EPILOG_WITH_TRANSITION_BLOCK_TAILCALL
1135     // Share patch label
1136     b ExternalMethodFixupPatchLabel
1137
1138 NESTED_END DelayLoad_MethodCall_FakeProlog, _TEXT
1139
1140
1141 .macro DynamicHelper frameFlags, suffix
1142 NESTED_ENTRY DelayLoad_Helper\suffix\()_FakeProlog, _TEXT, NoHandler
1143 DelayLoad_Helper\suffix:
1144     .global DelayLoad_Helper\suffix
1145
1146     PROLOG_WITH_TRANSITION_BLOCK
1147
1148     add x0, sp, #__PWTB_TransitionBlock // pTransitionBlock
1149     mov x1, x11 // Indirection cell
1150     mov x2, x9 // sectionIndex
1151     mov x3, x10 // Module*
1152     mov x4, \frameFlags
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):
1158     mov x12, x0
1159     EPILOG_WITH_TRANSITION_BLOCK_TAILCALL
1160     EPILOG_BRANCH_REG  x12
1161
1162 NESTED_END DelayLoad_Helper\suffix\()_FakeProlog, _TEXT
1163 .endm
1164
1165 DynamicHelper DynamicHelperFrameFlags_Default
1166 DynamicHelper DynamicHelperFrameFlags_ObjectArg, _Obj
1167 DynamicHelper DynamicHelperFrameFlags_ObjectArg | DynamicHelperFrameFlags_ObjectArg2, _ObjObj
1168 #endif
1169
1170 #ifdef FEATURE_PREJIT
1171 // ------------------------------------------------------------------
1172 // void StubDispatchFixupStub(args in regs x0-x7 & stack and possibly retbuff arg in x8, x11:IndirectionCellAndFlags)
1173 //
1174 // The stub dispatch thunk which transfers control to StubDispatchFixupWorker.
1175 NESTED_ENTRY StubDispatchFixupStub, _TEXT, NoHandler
1176
1177     PROLOG_WITH_TRANSITION_BLOCK
1178
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)
1184     mov x12, x0
1185
1186     EPILOG_WITH_TRANSITION_BLOCK_TAILCALL
1187     PATCH_LABEL StubDispatchFixupPatchLabel
1188     EPILOG_BRANCH_REG  x12
1189
1190 NESTED_END StubDispatchFixupStub, _TEXT
1191 #endif
1192
1193 #ifdef FEATURE_COMINTEROP
1194
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).
1197 //
1198 // On entry//
1199 //   x0          : size of the FP result (4 or 8 bytes)
1200 //   x1          : pointer to 64-bit buffer to receive result
1201 //
1202 // On exit:
1203 //   buffer pointed to by x1 on entry contains the float or double argument as appropriate
1204 //
1205 LEAF_ENTRY getFPReturn, _TEXT
1206     str d0, [x1]
1207 LEAF_END getFPReturn, _TEXT
1208
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).
1212 //
1213 // On entry:
1214 //   x0          : size of the FP result (4 or 8 bytes)
1215 //   x1          : 32-bit or 64-bit FP result
1216 //
1217 // On exit:
1218 //   s0          : float result if x0 == 4
1219 //   d0          : double result if x0 == 8
1220 //
1221 LEAF_ENTRY setFPReturn, _TEXT
1222     fmov d0, x1
1223 LEAF_END setFPReturn, _TEXT
1224 #endif
1225
1226 //
1227 // JIT Static access helpers when coreclr host specifies single appdomain flag 
1228 //
1229
1230 // ------------------------------------------------------------------
1231 // void* JIT_GetSharedNonGCStaticBase(SIZE_T moduleDomainID, DWORD dwClassDomainID)
1232
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]
1237     tst w2, #1
1238     beq LOCAL_LABEL(JIT_GetSharedNonGCStaticBase_SingleAppDomain_CallHelper)
1239
1240     ret lr
1241
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
1246
1247
1248 // ------------------------------------------------------------------
1249 // void* JIT_GetSharedNonGCStaticBaseNoCtor(SIZE_T moduleDomainID, DWORD dwClassDomainID)
1250
1251 LEAF_ENTRY JIT_GetSharedNonGCStaticBaseNoCtor_SingleAppDomain, _TEXT
1252     ret lr
1253 LEAF_END JIT_GetSharedNonGCStaticBaseNoCtor_SingleAppDomain, _TEXT
1254
1255
1256 // ------------------------------------------------------------------
1257 // void* JIT_GetSharedGCStaticBase(SIZE_T moduleDomainID, DWORD dwClassDomainID)
1258
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]
1263     tst w2, #1
1264     beq LOCAL_LABEL(JIT_GetSharedGCStaticBase_SingleAppDomain_CallHelper)
1265
1266     ldr x0, [x0, #DomainLocalModule__m_pGCStatics]
1267     ret lr
1268
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
1273
1274
1275 // ------------------------------------------------------------------
1276 // void* JIT_GetSharedGCStaticBaseNoCtor(SIZE_T moduleDomainID, DWORD dwClassDomainID)
1277
1278 LEAF_ENTRY JIT_GetSharedGCStaticBaseNoCtor_SingleAppDomain, _TEXT
1279     ldr x0, [x0, #DomainLocalModule__m_pGCStatics]
1280     ret lr
1281 LEAF_END JIT_GetSharedGCStaticBaseNoCtor_SingleAppDomain, _TEXT
1282
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
1287
1288     // check for null array
1289     cbz     x0, LOCAL_LABEL(ThrowNullReferenceException)
1290
1291     // idx bounds check
1292     ldr     x3,[x0,#ArrayBase__m_NumComponents]
1293     cmp     x3, x1
1294     bls     LOCAL_LABEL(ThrowIndexOutOfRangeException)
1295
1296     // fast path to null assignment (doesn't need any write-barriers)
1297     cbz     x2, LOCAL_LABEL(AssigningNull)
1298
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()
1303     cmp     x3, x12
1304     beq     C_FUNC(JIT_Stelem_DoWrite)
1305
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)
1311
1312     // array type and val type do not exactly match. Raise frame and do detailed match
1313     b       C_FUNC(JIT_Stelem_Ref_NotExactMatch)
1314
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
1319     ret
1320
1321 LOCAL_LABEL(ThrowNullReferenceException):
1322     // Tail call JIT_InternalThrow(NullReferenceException)
1323     ldr     x0, =CORINFO_NullReferenceException_ASM
1324     b       C_FUNC(JIT_InternalThrow)
1325
1326 LOCAL_LABEL(ThrowIndexOutOfRangeException):
1327     // Tail call JIT_InternalThrow(NullReferenceException)
1328     ldr     x0, =CORINFO_IndexOutOfRangeException_ASM
1329     b       C_FUNC(JIT_InternalThrow)
1330
1331 LEAF_END JIT_Stelem_Ref, _TEXT
1332
1333 // ------------------------------------------------------------------
1334 // __declspec(naked) void F_CALL_CONV JIT_Stelem_Ref_NotExactMatch(PtrArray* array,
1335 //                                                       unsigned idx, Object* val)
1336 //   x12 = array->GetArrayElementTypeHandle()
1337 //
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
1343
1344     // allow in case val can be casted to array element type
1345     // call ObjIsInstanceOfNoGC(val, array->GetArrayElementTypeHandle())
1346     mov     x1, x12 // array->GetArrayElementTypeHandle()
1347     mov     x0, x2
1348     bl      C_FUNC(ObjIsInstanceOfNoGC)
1349     cmp     x0, TypeHandle_CanCast
1350     beq     LOCAL_LABEL(DoWrite)             // ObjIsInstance returned TypeHandle::CanCast
1351
1352     // check via raising frame
1353 LOCAL_LABEL(NeedFrame):
1354     add     x1, sp, #16             // x1 = &array
1355     add     x0, sp, #32             // x0 = &val
1356
1357     bl      C_FUNC(ArrayStoreCheck) // ArrayStoreCheck(&val, &array)
1358
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
1365
1366 // ------------------------------------------------------------------
1367 // __declspec(naked) void F_CALL_CONV JIT_Stelem_DoWrite(PtrArray* array, unsigned idx, Object* val)
1368 LEAF_ENTRY  JIT_Stelem_DoWrite, _TEXT
1369
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
1374
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