[RISC-V] Fix hijack (#99809) (#449)
[platform/upstream/dotnet/runtime.git] / src / coreclr / vm / riscv64 / asmhelpers.S
1 // Licensed to the .NET Foundation under one or more agreements.
2 // The .NET Foundation llacenses this file to you under the MIT license.
3
4 #include "asmconstants.h"
5 #include "unixasmmacros.inc"
6
7 LEAF_ENTRY GetCurrentIP, _TEXT
8     addi  a0, ra, 0
9     ret
10 LEAF_END GetCurrentIP, _TEXT
11
12 // LPVOID __stdcall GetCurrentSP(void)//
13 LEAF_ENTRY GetCurrentSP, _TEXT
14     addi  a0, sp, 0
15     ret
16 LEAF_END GetCurrentSP, _TEXT
17
18 //-----------------------------------------------------------------------------
19 // The following Macros help in WRITE_BARRIER Implementations
20 // WRITE_BARRIER_ENTRY
21 //
22 // Declare the start of a write barrier function. Use similarly to NESTED_ENTRY. This is the only legal way
23 // to declare a write barrier function.
24 //
25 .macro WRITE_BARRIER_ENTRY name
26     LEAF_ENTRY \name, _TEXT
27 .endm
28
29 // WRITE_BARRIER_END
30 //
31 // The partner to WRITE_BARRIER_ENTRY, used llake NESTED_END.
32 //
33 .macro WRITE_BARRIER_END name
34     LEAF_END_MARKED \name, _TEXT
35 .endm
36
37 // void JIT_UpdateWriteBarrierState(bool skipEphemeralCheck, size_t writeableOffset)
38 //
39 // Update shadow copies of the various state info required for barrier
40 //
41 // State info is contained in a llateral pool at the end of the function
42 // Placed in text section so that it is close enough to use ldr llateral and still
43 // be relocatable. Ellaminates need for PREPARE_EXTERNAL_VAR in hot code.
44 //
45 // Allagn and group state info together so it fits in a single cache line
46 // and each entry can be written atomically
47 //
48 WRITE_BARRIER_ENTRY JIT_UpdateWriteBarrierState
49     // a0-a7 and t3 will contain intended new state
50     // t0 will preserve skipEphemeralCheck
51     // t2 will be used for pointers
52
53     addi  t0, a0, 0
54     addi  t1, a1, 0
55
56     lla  a0, g_card_table
57     ld  a0, 0(a0)
58
59 #ifdef FEATURE_MANUALLY_MANAGED_CARD_BUNDLES
60     lla  a1, g_card_bundle_table
61     ld  a1, 0(a1)
62 #endif
63
64 #ifdef WRITE_BARRIER_CHECK
65     lla  a2, g_GCShadow
66     ld  a2, 0(a2)
67
68     lla a3, g_GCShadowEnd
69     ld  a3, 0(a3)
70 #endif
71
72 #ifdef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
73     lla  a4, g_sw_ww_table
74     ld  a4, 0(a4)
75 #endif
76
77     lla  a5, g_ephemeral_low
78     ld  a5, 0(a5)
79
80     lla  a6, g_ephemeral_high
81     ld  a6, 0(a6)
82
83     beq  t0, zero, LOCAL_LABEL(EphemeralCheckEnabled)
84
85     ori  a5, zero, 0
86     addi  a6, zero, -1
87 LOCAL_LABEL(EphemeralCheckEnabled):
88
89     lla  a7, g_lowest_address
90     ld  a7, 0(a7)
91
92     lla  t3, g_highest_address
93     ld  t3, 0(t3)
94
95     // Update wbs state
96     lla  t2, JIT_WriteBarrier_Table_Loc
97     ld  t2, 0(t2)
98     add  t2, t2, t1
99
100     sd  a0, 0(t2)
101     sd  a1, 8(t2)
102     sd  a2, 16(t2)
103     sd  a3, 24(t2)
104     sd  a4, 32(t2)
105     sd  a5, 40(t2)
106     sd  a6, 48(t2)
107     sd  a7, 56(t2)
108     sd  t3, 64(t2)
109
110     EPILOG_RETURN
111
112 WRITE_BARRIER_END JIT_UpdateWriteBarrierState
113
114 // ----------------------------------------------------------------------------------------
115 // __declspec(naked) void F_CALL_CONV JIT_WriteBarrier_Callable(Object **dst, Object* val)
116 LEAF_ENTRY  JIT_WriteBarrier_Callable, _TEXT
117     // Setup args for JIT_WriteBarrier. a0 = dst ; a1 = val
118     addi  t3, a0, 0                 // t3 = dst
119     addi  t4, a1, 0                 // t4 = val
120
121     // Branch to the write barrier
122     lla  t1, JIT_WriteBarrier_Loc
123     ld  t1, 0(t1)
124     jr  t1
125 LEAF_END JIT_WriteBarrier_Callable, _TEXT
126
127
128 .balign 64  // Align to power of two at least as big as patchable literal pool so that it fits optimally in cache line
129 // ------------------------------------------------------------------
130 // Start of the writeable code region
131 LEAF_ENTRY JIT_PatchedCodeStart, _TEXT
132     ret
133 LEAF_END JIT_PatchedCodeStart, _TEXT
134
135 // void JIT_ByRefWriteBarrier
136 //
137 // On entry:
138 //   t5 : the source address (points to object reference to write)
139 //   t3: the destination address (object reference written here)
140 //
141 // On exit:
142 //   t5  : incremented by 8
143 //   t4  : trashed
144 //
145
146 // void JIT_ByRefWriteBarrier
147 WRITE_BARRIER_ENTRY JIT_ByRefWriteBarrier
148     ld  t4, 0(t5)
149     addi  t5, t5, 8
150     tail  C_FUNC(JIT_CheckedWriteBarrier)
151 WRITE_BARRIER_END JIT_ByRefWriteBarrier
152
153 //-----------------------------------------------------------------------------
154 // Simple WriteBarriers
155 // void JIT_CheckedWriteBarrier(Object** dst, Object* src)
156 //
157 // On entry:
158 //   t3 : the destination address (LHS of the assignment)
159 //   t4 : the object reference (RHS of the assignment)
160 //
161 // On exit:
162 //   t1  : trashed
163 //   t0  : trashed
164 //   t6  : trashed
165 //   t3  : trashed (incremented by 8 to implement JIT_ByRefWriteBarrier contract)
166 //
167
168 WRITE_BARRIER_ENTRY JIT_CheckedWriteBarrier
169     lla  t6, wbs_lowest_address
170     ld  t6, 0(t6)
171     slt  t6, t3, t6
172
173     lla  t1, wbs_highest_address
174     ld  t1, 0(t1)
175     slt  t0, t1, t3
176     or  t6, t0, t6
177     beq  t6, zero, C_FUNC(JIT_WriteBarrier)
178
179     sd  t4, 0(t3)
180     addi  t3, t3, 8
181     ret
182 WRITE_BARRIER_END JIT_CheckedWriteBarrier
183
184 // void JIT_WriteBarrier(Object** dst, Object* src)
185 // On entry:
186 //   t3  : the destination address (LHS of the assignment)
187 //   t4  1 the object reference (RHS of the assignment)
188 //
189 // On exit:
190 //   t0  : trashed
191 //   t1  : trashed
192 //   t6  : trashed
193 //   t4  : trashed
194 //   t3  : trashed (incremented by 8 to implement JIT_ByRefWriteBarrier contract)
195 //
196 WRITE_BARRIER_ENTRY JIT_WriteBarrier
197     // TODO: sync_release (runtime detection required)
198     fence rw, rw
199
200     sd  t4, 0(t3)
201
202 #ifdef WRITE_BARRIER_CHECK
203     // Update GC Shadow Heap
204
205     // Do not perform the work if g_GCShadow is 0
206     lla  t1, wbs_GCShadow
207     ld  t1, 0(t1)
208
209     beq  t1, zero, LOCAL_LABEL(ShadowUpdateDisabled)
210
211     // Compute address of shadow heap location:
212     //   pShadow = g_GCShadow + ($t3 - g_lowest_address)
213     lla  t6, wbs_lowest_address
214     ld  t6, 0(t6)
215
216     sub  t6, t3, t6
217     add  t0, t6, t1
218
219     // if (pShadow >= g_GCShadowEnd) goto end
220     lla t6, wbs_GCShadowEnd
221     ld  t6, 0(t6)
222
223     slt  t6, t0, t6
224     beq  t6, zero, LOCAL_LABEL(ShadowUpdateEnd)
225
226     // *pShadow = $t4
227     sd  t4, 0(t0)
228
229     // Ensure that the write to the shadow heap occurs before the read from the GC heap so that race
230     // conditions are caught by INVALIDGCVALUE.
231     fence rw, rw
232
233     // if (*t3 == t4) goto end
234     ld  t6, 0(t3)
235     beq  t6, t4, LOCAL_LABEL(ShadowUpdateEnd)
236
237     // *pShadow = INVALIDGCVALUE (0xcccccccd)
238     li  t6, 0xcccccccd
239     sd  t6, 0(t0)
240 LOCAL_LABEL(ShadowUpdateEnd):
241 LOCAL_LABEL(ShadowUpdateDisabled):
242 #endif
243
244 #ifdef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
245     // Update the write watch table if necessary
246
247     lla  t6, wbs_sw_ww_table
248     ld  t6, 0(t6)
249     beq  t6, zero, LOCAL_LABEL(CheckCardTable)
250
251     srli  t0, t3, 0xc
252     add  t6, t6, t0  // SoftwareWriteWatch::AddressToTableByteIndexShift
253     lb  t0, 0(t6)
254     bne  t0, zero, LOCAL_LABEL(CheckCardTable)
255
256     ori  t0, zero, 0xFF
257     sb  t0, 0(t6)
258
259 LOCAL_LABEL(CheckCardTable):
260 #endif
261     // Branch to Exit if the reference is not in the Gen0 heap
262     lla  t6, wbs_ephemeral_low
263     ld  t6, 0(t6)
264     beq  t6, zero, LOCAL_LABEL(SkipEphemeralCheck)
265
266     slt  t0, t4, t6
267     lla  t6, wbs_ephemeral_high
268     ld  t6, 0(t6)
269     slt  t1, t6, t4
270     or  t0, t1, t0
271     bne  t0, zero, LOCAL_LABEL(Exit)
272
273 LOCAL_LABEL(SkipEphemeralCheck):
274     // Check if we need to update the card table
275     lla  t6, wbs_card_table
276     ld  t6, 0(t6)
277     srli  t0, t3, 11
278     add  t4, t6, t0
279     lbu  t1, 0(t4)
280     ori  t0, zero, 0xFF
281     beq  t1, t0, LOCAL_LABEL(Exit)
282
283     sb  t0, 0(t4)
284
285 #ifdef FEATURE_MANUALLY_MANAGED_CARD_BUNDLES
286     // Check if we need to update the card bundle table
287     lla  t6, wbs_card_bundle_table
288     ld  t6, 0(t6)
289     srli  t0, t3, 21
290     add  t4, t6, t0
291
292     lbu  t6, 0(t4)
293     ori  t0, zero, 0xFF
294     beq  t6, t0, LOCAL_LABEL(Exit)
295
296     sb  t0, 0(t4)
297 #endif
298 LOCAL_LABEL(Exit):
299     addi  t3, t3, 8
300     ret
301 WRITE_BARRIER_END JIT_WriteBarrier
302
303 // Begin patchable literal pool
304     .balign 64  // Align to power of two at least as big as patchable literal pool so that it fits optimally in cache line
305 WRITE_BARRIER_ENTRY JIT_WriteBarrier_Table
306 wbs_begin:
307 wbs_card_table:
308     .quad 0
309 wbs_card_bundle_table:
310     .quad 0
311 wbs_GCShadow:
312     .quad 0
313 wbs_GCShadowEnd:
314     .quad 0
315 wbs_sw_ww_table:
316     .quad 0
317 wbs_ephemeral_low:
318     .quad 0
319 wbs_ephemeral_high:
320     .quad 0
321 wbs_lowest_address:
322     .quad 0
323 wbs_highest_address:
324     .quad 0
325 WRITE_BARRIER_END JIT_WriteBarrier_Table
326
327 // ------------------------------------------------------------------
328 // End of the writeable code region
329 LEAF_ENTRY JIT_PatchedCodeLast, _TEXT
330     ret
331 LEAF_END JIT_PatchedCodeLast, _TEXT
332
333
334 //
335 // If a preserved register were pushed onto the stack between
336 // the managed caller and the H_M_F, ptrS0_S8 will point to its
337 // location on the stack and it would have been updated on the
338 // stack by the GC already and it will be popped back into the
339 // appropriate register when the appropriate epilog is run.
340 //
341 // Otherwise, the register is preserved across all the code
342 // in this HCALL or FCALL, so we need to update those registers
343 // here because the GC will have updated our copies in the
344 // frame.
345 //
346 // So, if ptrS0_S8 points into the MachState, we need to update
347 // the register here.  That's what this macro does.
348 //
349 .macro RestoreRegMS idx, reg
350     // Incoming:
351     //
352     // a0 = address of MachState
353     //
354     // idx: Index of the callee register
355     // s0/fp: 0, s1: 1, s3-s11: 4-11, gp: 12 tp: 13
356     //
357     // reg: Register name (e.g. s0, s1, etc)
358     //
359     // Get the address of the specified captured register from machine state
360     addi  a2, a0, (MachState__captureCalleeSavedRegisters + (\idx * 8))
361
362     //// Get the content of specified preserved register pointer from machine state
363     ld  a3, (MachState__ptrCalleeSavedRegisters + (\idx * 8))(a0)
364
365     bne  a2, a3, LOCAL_LABEL(NoRestore_\reg)
366
367     ld  \reg, 0(a2)
368 LOCAL_LABEL(NoRestore_\reg):
369
370 .endm
371
372 NESTED_ENTRY ThePreStub, _TEXT, NoHandler
373     PROLOG_WITH_TRANSITION_BLOCK
374
375     addi  a1, METHODDESC_REGISTER, 0 // pMethodDesc
376
377     addi  a0, sp, __PWTB_TransitionBlock        // pTransitionBlock
378     call  PreStubWorker
379     addi  t4, a0, 0
380
381     EPILOG_WITH_TRANSITION_BLOCK_TAILCALL
382     EPILOG_BRANCH_REG  t4
383 NESTED_END ThePreStub, _TEXT
384
385 // ------------------------------------------------------------------\
386
387 // EXTERN_C int __fastcall HelperMethodFrameRestoreState(
388 // INDEBUG_COMMA(HelperMethodFrame *pFrame)
389 // MachState *pState
390 // )
391 LEAF_ENTRY HelperMethodFrameRestoreState, _TEXT
392 #ifdef _DEBUG
393     addi  a0, a1, 0
394 #endif
395
396     // If machine state is invalid, then simply exit
397     lw  a1, MachState__isValid(a0)
398     beq  a1, zero, LOCAL_LABEL(Done)
399
400     // manually assign index
401     // s0/fp: 0, s1: 1, s3-s11: 4-11, gp: 12 tp: 13
402     RestoreRegMS  0, s0 
403     RestoreRegMS  1, s1
404     RestoreRegMS  2, s2
405     RestoreRegMS  3, s3
406     RestoreRegMS  4, s4
407     RestoreRegMS  5, s5
408     RestoreRegMS  6, s6
409     RestoreRegMS  7, s7
410     RestoreRegMS  8, s8
411     RestoreRegMS  9, s9
412     RestoreRegMS  10, s10
413     RestoreRegMS  11, s11
414     RestoreRegMS  12, gp
415     RestoreRegMS  13, tp
416 LOCAL_LABEL(Done):
417     // Its imperative that the return value of HelperMethodFrameRestoreState is zero
418     // as it is used in the state machine to loop until it becomes zero.
419     // Refer to HELPER_METHOD_FRAME_END macro for details.
420     addi  a0, zero, 0
421     ret
422 LEAF_END HelperMethodFrameRestoreState, _TEXT
423
424 //-----------------------------------------------------------------------------
425 // This routine captures the machine state. It is used by helper method frame
426 //-----------------------------------------------------------------------------
427 //void LazyMachStateCaptureState(struct LazyMachState *pState)//
428 LEAF_ENTRY LazyMachStateCaptureState, _TEXT
429     // marks that this is not yet valid
430     sw  zero, (MachState__isValid)(a0)
431
432     sd  ra, (LazyMachState_captureIp)(a0)
433
434     // save sp register.
435     sd  sp, (LazyMachState_captureSp)(a0)
436
437     // save non-volatile registers that can contain object references
438     addi  a1, a0, LazyMachState_captureCalleeSavedRegisters
439
440     sd  s0, 0(a1)
441     sd  s1, 8(a1)
442     sd  s2, 16(a1)
443     sd  s3, 24(a1)
444     sd  s4, 32(a1)
445     sd  s5, 40(a1)
446     sd  s6, 48(a1)
447     sd  s7, 56(a1)
448     sd  s8, 64(a1)
449     sd  s9, 72(a1)
450     sd  s10, 80(a1)
451     sd  s11, 88(a1)
452     sd  gp, 96(a1)
453     sd  tp, 104(a1)
454
455     ret
456 LEAF_END LazyMachStateCaptureState, _TEXT
457
458 // ------------------------------------------------------------------
459 // The call in ndirect import precode points to this function.
460 NESTED_ENTRY NDirectImportThunk, _TEXT, NoHandler
461     PROLOG_SAVE_REG_PAIR_INDEXED  fp, ra, 0xa0
462     SAVE_ARGUMENT_REGISTERS  sp, 0x20
463     SAVE_FLOAT_ARGUMENT_REGISTERS  sp, 0x60
464
465     addi  a0, t2, 0
466     call C_FUNC(NDirectImportWorker)
467     addi  t4, a0, 0
468
469     // pop the stack and restore original register state
470     RESTORE_FLOAT_ARGUMENT_REGISTERS  sp, 0x60
471     RESTORE_ARGUMENT_REGISTERS  sp, 0x20
472     //EPILOG_RESTORE_REG  gp, 16
473     EPILOG_RESTORE_REG_PAIR_INDEXED  fp, ra, 0xa0
474
475     // If we got back from NDirectImportWorker, the MD has been successfully
476     // linked. Proceed to execute the original DLL call.
477     EPILOG_BRANCH_REG  t4
478 NESTED_END NDirectImportThunk, _TEXT
479
480 // void SinglecastDelegateInvokeStub(Delegate *pThis)
481 LEAF_ENTRY SinglecastDelegateInvokeStub, _TEXT
482     beq  a0, zero, LOCAL_LABEL(LNullThis)
483
484     ld  t4, (DelegateObject___methodPtr)(a0)
485     ld  a0, (DelegateObject___target)(a0)
486     jr  t4
487
488 LOCAL_LABEL(LNullThis):
489     addi  a0, zero, CORINFO_NullReferenceException_ASM
490     tail JIT_InternalThrow
491 LEAF_END SinglecastDelegateInvokeStub, _TEXT
492
493 // ------------------------------------------------------------------
494 // ThePreStubPatch()
495 LEAF_ENTRY ThePreStubPatch, _TEXT
496 .globl C_FUNC(ThePreStubPatchLabel)
497 C_FUNC(ThePreStubPatchLabel):
498     ret
499 LEAF_END ThePreStubPatch, _TEXT
500
501 NESTED_ENTRY TheUMEntryPrestub, _TEXT, UnhandledExceptionHandlerUnix
502     // Save arguments and return address
503     PROLOG_SAVE_REG_PAIR_INDEXED  fp, ra, 0xa0
504     //PROLOG_SAVE_REG  gp, 16
505     SAVE_ARGUMENT_REGISTERS  sp, 32
506     SAVE_FLOAT_ARGUMENT_REGISTERS  sp, 96
507
508
509     addi  a0, t2, 0
510     call  TheUMEntryPrestubWorker
511     addi  t4, a0, 0
512
513     // pop the stack and restore original register state
514     RESTORE_FLOAT_ARGUMENT_REGISTERS  sp, 96
515     RESTORE_ARGUMENT_REGISTERS  sp, 32
516     //EPILOG_RESTORE_REG  gp, 16
517     EPILOG_RESTORE_REG_PAIR_INDEXED  fp, ra, 0xa0
518
519     // and tailcall to the actual method
520     EPILOG_BRANCH_REG t4
521 NESTED_END TheUMEntryPrestub, _TEXT
522
523 // ------------------------------------------------------------------
524 // void* JIT_GetSharedGCStaticBase(SIZE_T moduleDomainID, DWORD dwClassDomainID)
525
526 LEAF_ENTRY JIT_GetSharedGCStaticBase_SingleAppDomain, _TEXT
527     // If class is not initialized, bail to C++ helper
528     addi  a2, a0, DomainLocalModule__m_pDataBlob
529     add  a2, a2, a1
530     lb  a2, 0(a2)
531     andi  t5, a2, 1
532     beq  t5, zero, LOCAL_LABEL(JIT_GetSharedGCStaticBase_SingleAppDomain_CallHelper)
533
534     ld  a0, DomainLocalModule__m_pGCStatics(a0)
535     ret
536
537 LOCAL_LABEL(JIT_GetSharedGCStaticBase_SingleAppDomain_CallHelper):
538     // Tail call JIT_GetSharedGCStaticBase_Helper
539     call  JIT_GetSharedGCStaticBase_Helper
540 LEAF_END JIT_GetSharedGCStaticBase_SingleAppDomain, _TEXT
541
542 // Make sure the `FaultingExceptionFrame_StackAlloc` is 16-byte aligned.
543 #define FaultingExceptionFrame_StackAlloc (SIZEOF__GSCookie + SIZEOF__FaultingExceptionFrame + 0x8)
544 #define FaultingExceptionFrame_FrameOffset SIZEOF__GSCookie
545
546 .macro GenerateRedirectedStubWithFrame stub, target
547
548     //
549     // This is the primary function to which execution will be redirected to.
550     //
551     NESTED_ENTRY \stub, _TEXT, NoHandler
552
553         //
554         // IN: ra: original IP before redirect
555         //
556
557         PROLOG_SAVE_REG_PAIR_INDEXED  fp, ra, 16
558
559         // alloc stack for FaultingExceptionFrame.
560         addi  sp, sp, -FaultingExceptionFrame_StackAlloc
561
562         // stack must be 16 bytes aligned
563         CHECK_STACK_ALIGNMENT
564
565         // Save pointer to FEF for GetFrameFromRedirectedStubStackFrame
566         addi  a0, sp, FaultingExceptionFrame_FrameOffset
567
568         // Prepare to initialize to NULL
569         sd    zero, 0(a0)  // Initialize vtbl (it is not strictly necessary)
570         sd    zero, FaultingExceptionFrame__m_fFilterExecuted(a0)  // Initialize BOOL for personality routine
571
572         call   C_FUNC(\target)
573         // Target should not return.
574         EMIT_BREAKPOINT
575
576     NESTED_END \stub, _TEXT
577
578 .endm
579
580 GenerateRedirectedStubWithFrame RedirectForThreadAbort, ThrowControlForThread
581
582 // ------------------------------------------------------------------
583 // ResolveWorkerChainLookupAsmStub
584 //
585 // This method will perform a quick chained lookup of the entry if the
586 //  initial cache lookup fails.
587 //
588 // On Entry:
589 //   t1       contains the pointer to the current ResolveCacheElem
590 //   t5       contains the address of the indirection (and the flags in the low two bits)
591 //   t2       contains our contract the DispatchToken
592 // Must be preserved:
593 //   a0       contains the instance object ref that we are making an interface call on
594 //   t1       Must point to a ResolveCacheElem [For Sanity]
595 //  [a1-a7]   contains any additional register arguments for the interface method
596 //
597 // Loaded from a0
598 //   t6       contains our type     the MethodTable  (from object ref in a0)
599 //
600 // On Exit:
601 //   a0, [a1-a7] arguments for the interface implementation target
602 //
603 // On Exit (to ResolveWorkerAsmStub):
604 //   t5       contains the address of the indirection and the flags in the low two bits.
605 //   t2       contains our contract (DispatchToken)
606 //   t4 will be trashed
607 //
608
609 #define BACKPATCH_FLAG      1
610 #define PROMOTE_CHAIN_FLAG  2
611
612 NESTED_ENTRY ResolveWorkerChainLookupAsmStub, _TEXT, NoHandler
613     andi  t4, t5, BACKPATCH_FLAG     // First we check if t5 has the BACKPATCH_FLAG set
614     bne  t4, zero, LOCAL_LABEL(Fail) // If the BACKPATCH_FLAGS is set we will go directly to the ResolveWorkerAsmStub
615
616     ld  t6, 0(a0)         // retrieve the MethodTable from the object ref in a0
617 LOCAL_LABEL(MainLoop):
618     ld  t1, (ResolveCacheElem__pNext)(t1)     // t1 <= the next entry in the chain
619     beq  t1, zero, LOCAL_LABEL(Fail)
620
621     ld  t4, 0(t1)
622     // compare our MT with the one in the ResolveCacheElem
623     bne  t4, t6, LOCAL_LABEL(MainLoop)
624
625     ld  t4, 8(t1)
626     // compare our DispatchToken with one in the ResolveCacheElem
627     bne  t2, t4, LOCAL_LABEL(MainLoop)
628
629 LOCAL_LABEL(Success):
630     PREPARE_EXTERNAL_VAR  g_dispatch_cache_chain_success_counter, t6
631     ld  t4, 0(t6)
632     addi t4, t4, -1
633     sd  t4, 0(t6)
634     blt t4, zero, LOCAL_LABEL(Promote)
635
636     ld  t4, (ResolveCacheElem__target)(t1)    // get the ImplTarget
637     jr  t4                                    // branch to interface implementation target
638
639 LOCAL_LABEL(Promote):
640                           // Move this entry to head position of the chain
641     addi  t4, zero, 256
642     sd  t4, 0(t6)        // be quick to reset the counter so we don't get a bunch of contending threads
643     ori  t5, t5, PROMOTE_CHAIN_FLAG   // set PROMOTE_CHAIN_FLAG
644     addi  t2, t1, 0           // We pass the ResolveCacheElem to ResolveWorkerAsmStub instead of the DispatchToken
645
646 LOCAL_LABEL(Fail):
647     tail  C_FUNC(ResolveWorkerAsmStub) // call the ResolveWorkerAsmStub method to transition into the VM
648 NESTED_END ResolveWorkerChainLookupAsmStub, _TEXT
649
650 // ------------------------------------------------------------------
651 // void ResolveWorkerAsmStub(args in regs a0-a7 & stack, t5:IndirectionCellAndFlags, t2:DispatchToken)
652 //
653 // The stub dispatch thunk which transfers control to VSD_ResolveWorker.
654 NESTED_ENTRY ResolveWorkerAsmStub, _TEXT, NoHandler
655     PROLOG_WITH_TRANSITION_BLOCK
656
657     addi  a2, t2, 0                 // DispatchToken
658     addi  a0, sp, __PWTB_TransitionBlock        // pTransitionBlock
659     srli  a1, t5, 2
660     andi  a3, t5, 3              // flag
661     slli  a1, a1, 2
662     call C_FUNC(VSD_ResolveWorker)
663     addi  t4, a0, 0
664
665     EPILOG_WITH_TRANSITION_BLOCK_TAILCALL
666
667     EPILOG_BRANCH_REG  t4
668 NESTED_END ResolveWorkerAsmStub, _TEXT
669
670 // ------------------------------------------------------------------
671 // void* JIT_GetSharedNonGCStaticBaseNoCtor(SIZE_T moduleDomainID, DWORD dwClassDomainID)
672
673 LEAF_ENTRY JIT_GetSharedNonGCStaticBaseNoCtor_SingleAppDomain, _TEXT
674     ret
675 LEAF_END JIT_GetSharedNonGCStaticBaseNoCtor_SingleAppDomain, _TEXT
676
677 // ------------------------------------------------------------------
678 // void* JIT_GetSharedGCStaticBaseNoCtor(SIZE_T moduleDomainID, DWORD dwClassDomainID)
679
680 LEAF_ENTRY JIT_GetSharedGCStaticBaseNoCtor_SingleAppDomain, _TEXT
681     ld  a0, (DomainLocalModule__m_pGCStatics)(a0)
682     ret
683 LEAF_END JIT_GetSharedGCStaticBaseNoCtor_SingleAppDomain, _TEXT
684
685
686 #ifdef FEATURE_HIJACK
687 // ------------------------------------------------------------------
688 // Hijack function for functions which return a scalar type or a struct (value type)
689 NESTED_ENTRY OnHijackTripThread, _TEXT, NoHandler
690     PROLOG_SAVE_REG_PAIR_INDEXED   fp, ra, 0xa0
691
692     // Spill callee saved registers
693     PROLOG_SAVE_REG_PAIR   s1, s2, 16
694     PROLOG_SAVE_REG_PAIR   s3, s4, 32
695     PROLOG_SAVE_REG_PAIR   s5, s6, 48
696     PROLOG_SAVE_REG_PAIR   s7, s8, 64
697     PROLOG_SAVE_REG_PAIR   s9, s10, 80
698     PROLOG_SAVE_REG_PAIR   s11, gp, 96
699     PROLOG_SAVE_REG        tp, 112
700
701     // save any integral return value(s)
702     sd  a0, 120(sp)
703     sd  a1, 128(sp)
704
705     // save any FP/HFA return value(s)
706     fsd  f0, 136(sp)
707     fsd  f1, 144(sp)
708
709     addi  a0, sp, 0
710     call  C_FUNC(OnHijackWorker)
711
712     // restore callee saved registers
713
714     // restore any integral return value(s)
715     ld  a0, 120(sp)
716     ld  a1, 128(sp)
717
718     // restore any FP/HFA return value(s)
719     fld  f0, 136(sp)
720     fld  f1, 144(sp)
721
722     EPILOG_RESTORE_REG_PAIR   s1, s2, 16
723     EPILOG_RESTORE_REG_PAIR   s3, s4, 32
724     EPILOG_RESTORE_REG_PAIR   s5, s6, 48
725     EPILOG_RESTORE_REG_PAIR   s7, s8, 64
726     EPILOG_RESTORE_REG_PAIR   s9, s10, 80
727     EPILOG_RESTORE_REG_PAIR   s11, gp, 96
728     EPILOG_RESTORE_REG        tp, 112
729     EPILOG_RESTORE_REG_PAIR_INDEXED  fp, ra, 0xa0
730     EPILOG_RETURN
731 NESTED_END OnHijackTripThread, _TEXT
732
733 #endif // FEATURE_HIJACK
734
735 // ------------------------------------------------------------------
736 // Redirection Stub for GC in fully interruptible method
737 //GenerateRedirectedHandledJITCaseStub GCThreadControl
738 // ------------------------------------------------------------------
739 //GenerateRedirectedHandledJITCaseStub DbgThreadControl
740 // ------------------------------------------------------------------
741 //GenerateRedirectedHandledJITCaseStub UserSuspend
742
743 #ifdef _DEBUG
744 // ------------------------------------------------------------------
745 // Redirection Stub for GC Stress
746 GenerateRedirectedHandledJITCaseStub GCStress
747 #endif
748
749
750 // ------------------------------------------------------------------
751 // This helper enables us to call into a funclet after restoring Fp register
752 NESTED_ENTRY CallEHFunclet, _TEXT, NoHandler
753     // On entry:
754     //
755     // a0 = throwable
756     // a1 = PC to invoke
757     // a2 = address of s0 register in CONTEXT record// used to restore the non-volatile registers of CrawlFrame
758     // a3 = address of the location where the SP of funclet's caller (i.e. this helper) should be saved.
759     //
760
761     PROLOG_SAVE_REG_PAIR_INDEXED   fp, ra, 128, 0
762
763     // Spill callee saved registers
764     PROLOG_SAVE_REG_PAIR   s1, s2, 16
765     PROLOG_SAVE_REG_PAIR   s3, s4, 32
766     PROLOG_SAVE_REG_PAIR   s5, s6, 48
767     PROLOG_SAVE_REG_PAIR   s7, s8, 64
768     PROLOG_SAVE_REG_PAIR   s9, s10, 80 
769     PROLOG_SAVE_REG_PAIR   s11, gp, 96
770     PROLOG_SAVE_REG tp, 112
771
772     // Save the SP of this function
773     sd  sp, 0(a3)
774
775     ld  gp, (-40)(a2)  // offset of tp in PCONTEXT relative to S0.
776     ld  tp, (-32)(a2)  // offset of tp in PCONTEXT relative to S0.
777     ld  fp, 0(a2)  // offset of fp in PCONTEXT relative to S0.
778     ld  s1, 8(a2)
779     ld  s2, 80(a2)
780     ld  s3, 88(a2)
781     ld  s4, 96(a2)
782     ld  s5, 104(a2)
783     ld  s6, 112(a2)
784     ld  s7, 120(a2)
785     ld  s8, 128(a2)
786     ld  s9, 136(a2)
787     ld  s10, 144(a2)
788     ld  s11, 152(a2)
789
790     // Invoke the funclet
791     jalr a1
792
793     EPILOG_RESTORE_REG_PAIR   s1, s2, 16
794     EPILOG_RESTORE_REG_PAIR   s3, s4, 32
795     EPILOG_RESTORE_REG_PAIR   s5, s6, 48
796     EPILOG_RESTORE_REG_PAIR   s7, s8, 64
797     EPILOG_RESTORE_REG_PAIR   s9, s10, 80
798     EPILOG_RESTORE_REG_PAIR   s11, gp, 96
799     EPILOG_RESTORE_REG tp, 112
800
801     EPILOG_RESTORE_REG_PAIR_INDEXED   fp, ra, 128
802     EPILOG_RETURN
803 NESTED_END CallEHFunclet, _TEXT
804
805 // This helper enables us to call into a filter funclet by passing it the CallerSP to lookup the
806 // frame pointer for accessing the locals in the parent method.
807 NESTED_ENTRY CallEHFilterFunclet, _TEXT, NoHandler
808     PROLOG_SAVE_REG_PAIR_INDEXED   fp, ra, 16
809
810     // On entry:
811     //
812     // a0 = throwable
813     // a1 = SP of the caller of the method/funclet containing the filter
814     // a2 = PC to invoke
815     // a3 = address of the location where the SP of funclet's caller (i.e. this helper) should be saved.
816     //
817     // Save the SP of this function
818     sd  fp, 0(a3)
819     // Invoke the filter funclet
820     jalr a2
821
822     EPILOG_RESTORE_REG_PAIR_INDEXED   fp, ra, 16
823     EPILOG_RETURN
824 NESTED_END CallEHFilterFunclet, _TEXT
825
826 #ifdef FEATURE_COMINTEROP
827 // Function used by COM interop to get floating point return value (since it's not in the same
828 // register(s) as non-floating point values).
829 //
830 // On entry//
831 //   a0          : size of the FP result (4 or 8 bytes)
832 //   a1          : pointer to 64-bit buffer to receive result
833 //
834 // On exit:
835 //   buffer pointed to by a1 on entry contains the float or double argument as appropriate
836 //
837 LEAF_ENTRY getFPReturn, _TEXT
838     fsd  f0, 0(a1)
839 LEAF_END getFPReturn, _TEXT
840
841 // ------------------------------------------------------------------
842 // Function used by COM interop to set floating point return value (since it's not in the same
843 // register(s) as non-floating point values).
844 //
845 LEAF_ENTRY setFPReturn, _TEXT
846     fmv.d.x f0, a1
847 LEAF_END setFPReturn, _TEXT
848
849 #endif // FEATURE_COMINTEROP
850
851 //
852 // JIT Static access helpers when coreclr host specifies single appdomain flag
853 //
854
855 // ------------------------------------------------------------------
856 // void* JIT_GetSharedNonGCStaticBase(SIZE_T moduleDomainID, DWORD dwClassDomainID)
857
858 LEAF_ENTRY JIT_GetSharedNonGCStaticBase_SingleAppDomain, _TEXT
859     // If class is not initialized, bail to C++ helper
860     // dext a1, a1, 0, 32
861     addi  a2, a0, DomainLocalModule__m_pDataBlob
862
863     add a2, a2, a1
864     lb a2, 0(a2)
865     andi  t4, a2, 1
866     beq  t4, zero, LOCAL_LABEL(JIT_GetSharedNonGCStaticBase_SingleAppDomain_CallHelper)
867
868     ret
869
870 LOCAL_LABEL(JIT_GetSharedNonGCStaticBase_SingleAppDomain_CallHelper):
871     // Tail call JIT_GetSharedNonGCStaticBase_Helper
872     tail  JIT_GetSharedNonGCStaticBase_Helper
873 LEAF_END JIT_GetSharedNonGCStaticBase_SingleAppDomain, _TEXT
874
875 #ifdef FEATURE_READYTORUN
876
877 NESTED_ENTRY DelayLoad_MethodCall_FakeProlog, _TEXT, NoHandler
878 C_FUNC(DelayLoad_MethodCall):
879     .global C_FUNC(DelayLoad_MethodCall)
880     PROLOG_WITH_TRANSITION_BLOCK
881
882     addi  a1, t5, 0      // Indirection cell
883     addi  a2, t0, 0      // sectionIndex
884     addi  a3, t1, 0      // Module*
885
886     addi  a0, sp, __PWTB_TransitionBlock        // pTransitionBlock
887     call  C_FUNC(ExternalMethodFixupWorker)
888     addi  t4, a0, 0
889
890     EPILOG_WITH_TRANSITION_BLOCK_TAILCALL
891     PATCH_LABEL ExternalMethodFixupPatchLabel
892     EPILOG_BRANCH_REG   t4
893 NESTED_END DelayLoad_MethodCall_FakeProlog, _TEXT
894
895
896 .macro DynamicHelper frameFlags, suffix
897 NESTED_ENTRY DelayLoad_Helper\suffix\()_FakeProlog, _TEXT, NoHandler
898 DelayLoad_Helper\suffix:
899     .global DelayLoad_Helper\suffix
900
901     PROLOG_WITH_TRANSITION_BLOCK
902
903     //DynamicHelperWorker(TransitionBlock * pTransitionBlock, TADDR * pCell,
904     //                    DWORD sectionIndex, Module * pModule, INT frameFlags)
905     addi  a1, t5, 0      // Indirection cell
906     addi  a2, t0, 0      // sectionIndex
907     addi  a3, t1, 0      // Module*
908     addi  a4, x0, \frameFlags
909
910     addi  a0, sp, __PWTB_TransitionBlock        // pTransitionBlock
911     call  DynamicHelperWorker
912
913     bne a0, x0, LOCAL_LABEL(FakeProlog\suffix\()_0)
914
915     ld  a0, __PWTB_ArgumentRegisters(sp)
916     EPILOG_WITH_TRANSITION_BLOCK_RETURN
917
918 LOCAL_LABEL(FakeProlog\suffix\()_0):
919     addi t4, a0, 0
920     EPILOG_WITH_TRANSITION_BLOCK_TAILCALL
921     EPILOG_BRANCH_REG  t4
922
923 NESTED_END DelayLoad_Helper\suffix\()_FakeProlog, _TEXT
924 .endm
925
926 DynamicHelper DynamicHelperFrameFlags_Default
927 DynamicHelper DynamicHelperFrameFlags_ObjectArg, _Obj
928 DynamicHelper DynamicHelperFrameFlags_ObjectArg | DynamicHelperFrameFlags_ObjectArg2, _ObjObj
929 #endif
930
931
932 #ifdef PROFILING_SUPPORTED
933
934 // ------------------------------------------------------------------
935 LEAF_ENTRY JIT_ProfilerEnterLeaveTailcallStub, _TEXT
936     ret
937 LEAF_END JIT_ProfilerEnterLeaveTailcallStub, _TEXT
938
939 // ------------------------------------------------------------------
940 .macro GenerateProfileHelper helper, flags
941 NESTED_ENTRY \helper\()Naked, _TEXT, NoHandler
942     // On entry:
943     //   t0 = functionIDOrClientID
944     //   t1 = profiledSp
945     //   t6 = throwable
946     //
947     // On exit:
948     //   Values of a0-a7, fa0-fa7, fp are preserved.
949     //   Values of other volatile registers are not preserved.
950
951     // Fill in PROFILE_PLATFORM_SPECIFIC_DATA struct
952     PROLOG_SAVE_REG_PAIR_INDEXED fp, ra, SIZEOF__PROFILE_PLATFORM_SPECIFIC_DATA // Allocate space and save Fp, Pc.
953     SAVE_ARGUMENT_REGISTERS sp, PROFILE_PLATFORM_SPECIFIC_DATA__argumentRegisters
954     sd     zero, PROFILE_PLATFORM_SPECIFIC_DATA__functionId(sp)
955     SAVE_FLOAT_ARGUMENT_REGISTERS sp, PROFILE_PLATFORM_SPECIFIC_DATA__floatArgumentRegisters
956     addi     t6, sp, SIZEOF__PROFILE_PLATFORM_SPECIFIC_DATA // Compute probeSp - initial value of Sp on entry to the helper.
957     sd  t6, PROFILE_PLATFORM_SPECIFIC_DATA__probeSp(sp)
958     sd  t1, PROFILE_PLATFORM_SPECIFIC_DATA__profiledSp(sp)
959     sd  zero, PROFILE_PLATFORM_SPECIFIC_DATA__hiddenArg(sp)
960     addi  t6, zero, \flags
961     sd  t6, PROFILE_PLATFORM_SPECIFIC_DATA__flags(sp)
962
963     addi  a0, t0, 0
964     addi  a1, sp, 0
965     call  C_FUNC(\helper)
966
967     RESTORE_ARGUMENT_REGISTERS sp, PROFILE_PLATFORM_SPECIFIC_DATA__argumentRegisters
968     RESTORE_FLOAT_ARGUMENT_REGISTERS sp, PROFILE_PLATFORM_SPECIFIC_DATA__floatArgumentRegisters
969     EPILOG_RESTORE_REG_PAIR_INDEXED fp, ra, SIZEOF__PROFILE_PLATFORM_SPECIFIC_DATA
970     EPILOG_RETURN
971
972 NESTED_END \helper\()Naked, _TEXT
973 .endm
974
975 GenerateProfileHelper ProfileEnter, PROFILE_ENTER
976 GenerateProfileHelper ProfileLeave, PROFILE_LEAVE
977 GenerateProfileHelper ProfileTailcall, PROFILE_TAILCALL
978
979 #endif // PROFILING_SUPPORTED
980
981
982 #ifdef FEATURE_TIERED_COMPILATION
983
984 NESTED_ENTRY OnCallCountThresholdReachedStub, _TEXT, NoHandler
985     PROLOG_WITH_TRANSITION_BLOCK
986
987     addi  a0, sp, __PWTB_TransitionBlock // TransitionBlock *
988     addi  a1, t3, 0 // stub-identifying token
989     call  C_FUNC(OnCallCountThresholdReached)
990     addi  t4, a0, 0
991
992     EPILOG_WITH_TRANSITION_BLOCK_TAILCALL
993     EPILOG_BRANCH_REG t4
994 NESTED_END OnCallCountThresholdReachedStub, _TEXT
995
996 #endif // FEATURE_TIERED_COMPILATION
997
998 // ------------------------------------------------------------------
999 // size_t GetThreadStaticsVariableOffset()
1000
1001 // Load offset of native thread local variable `t_ThreadStatics` in TCB and return it in `a0` register.
1002 LEAF_ENTRY GetThreadStaticsVariableOffset, _TEXT
1003     la.tls.ie   a0, t_ThreadStatics
1004     EPILOG_RETURN
1005 LEAF_END GetThreadStaticsVariableOffset, _TEXT