Build tests in gbs for x86/x64 (#307)
[platform/upstream/coreclr.git] / src / vm / arm64 / stubs.cpp
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 // File: stubs.cpp
6 //
7 // This file contains stub functions for unimplemented features need to
8 // run on the ARM64 platform.
9
10 #include "common.h"
11 #include "dllimportcallback.h"
12 #include "comdelegate.h"
13 #include "asmconstants.h"
14 #include "virtualcallstub.h"
15 #include "jitinterface.h"
16 #include "ecall.h"
17
18 EXTERN_C void JIT_UpdateWriteBarrierState(bool skipEphemeralCheck);
19
20
21 #ifndef DACCESS_COMPILE
22 //-----------------------------------------------------------------------
23 // InstructionFormat for B.cond
24 //-----------------------------------------------------------------------
25 class ConditionalBranchInstructionFormat : public InstructionFormat
26 {
27
28     public:
29         ConditionalBranchInstructionFormat() : InstructionFormat(InstructionFormat::k32)
30         {
31             LIMITED_METHOD_CONTRACT;
32         }
33
34         virtual UINT GetSizeOfInstruction(UINT refsize, UINT variationCode)
35         {
36             LIMITED_METHOD_CONTRACT;
37
38             _ASSERTE(refsize == InstructionFormat::k32);
39
40             return 4;
41         }
42
43         virtual UINT GetHotSpotOffset(UINT refsize, UINT variationCode)
44         {
45             WRAPPER_NO_CONTRACT;
46             return 0;
47         }
48
49
50         virtual BOOL CanReach(UINT refSize, UINT variationCode, BOOL fExternal, INT_PTR offset)
51         {
52             _ASSERTE(!fExternal || "ARM64:NYI - CompareAndBranchInstructionFormat::CanReach external");
53             if (fExternal)
54                 return false;
55
56             if (offset < -1048576 || offset > 1048572)
57                 return false;
58             return true;
59         }
60         // B.<cond> <label>
61         // Encoding 0|1|0|1|0|1|0|0|imm19|0|cond
62         // cond = Bits3-0(variation)
63         // imm19 = bits19-0(fixedUpReference/4), will be SignExtended
64         virtual VOID EmitInstruction(UINT refSize, __int64 fixedUpReference, BYTE *pOutBuffer, UINT variationCode, BYTE *pDataBuffer)
65         {
66             LIMITED_METHOD_CONTRACT;
67
68             _ASSERTE(refSize == InstructionFormat::k32);
69
70             if (fixedUpReference < -1048576 || fixedUpReference > 1048572)
71                 COMPlusThrow(kNotSupportedException);
72
73             _ASSERTE((fixedUpReference & 0x3) == 0);
74             DWORD imm19 = (DWORD)(0x7FFFF & (fixedUpReference >> 2));
75
76             pOutBuffer[0] = static_cast<BYTE>((0x7 & imm19 /* Bits2-0(imm19) */) << 5  | (0xF & variationCode /* cond */));
77             pOutBuffer[1] = static_cast<BYTE>((0x7F8 & imm19 /* Bits10-3(imm19) */) >> 3);
78             pOutBuffer[2] = static_cast<BYTE>((0x7F800 & imm19 /* Bits19-11(imm19) */) >> 11);
79             pOutBuffer[3] = static_cast<BYTE>(0x54);
80         }
81 };
82
83 //-----------------------------------------------------------------------
84 // InstructionFormat for B(L)(R) (unconditional branch)
85 //-----------------------------------------------------------------------
86 class BranchInstructionFormat : public InstructionFormat
87 {
88     // Encoding of the VariationCode:
89     // bit(0) indicates whether this is a direct or an indirect jump.
90     // bit(1) indicates whether this is a branch with link -a.k.a call- (BL(R)) or not (B(R))
91     // bit(2) indicates whether this is a relative indirect branch or not
92
93     public:
94         enum VariationCodes
95         {
96             BIF_VAR_INDIRECT           = 0x00000001,
97             BIF_VAR_CALL               = 0x00000002,
98
99             BIF_VAR_JUMP               = 0x00000000,
100             BIF_VAR_INDIRECT_CALL      = 0x00000003,
101
102             BIF_VAR_RELATIVE_INDIRECT  = 0x00000004
103         };
104     private:
105         BOOL IsIndirect(UINT variationCode)
106         {
107             return (variationCode & BIF_VAR_INDIRECT) != 0;
108         }
109         BOOL IsCall(UINT variationCode)
110         {
111             return (variationCode & BIF_VAR_CALL) != 0;
112         }
113         BOOL IsRelativeIndirect(UINT variationCode)
114         {
115             BOOL result = (variationCode & BIF_VAR_RELATIVE_INDIRECT) != 0;
116             _ASSERTE(result && IsIndirect(variationCode) || !result);
117             return result;
118         }
119
120
121     public:
122         BranchInstructionFormat() : InstructionFormat(InstructionFormat::k64)
123         {
124             LIMITED_METHOD_CONTRACT;
125         }
126
127         virtual UINT GetSizeOfInstruction(UINT refSize, UINT variationCode)
128         {
129             LIMITED_METHOD_CONTRACT;
130             _ASSERTE(refSize == InstructionFormat::k64);
131
132             if (IsRelativeIndirect(variationCode))
133                 return 20;
134             else if (IsIndirect(variationCode))
135                 return 12;
136             else
137                 return 8;
138         }
139
140         virtual UINT GetSizeOfData(UINT refSize, UINT variationCode)
141         {
142             WRAPPER_NO_CONTRACT;
143             return 8;
144         }
145
146         virtual UINT GetHotSpotOffset(UINT refsize, UINT variationCode)
147         {
148             WRAPPER_NO_CONTRACT;
149             return 0;
150         }
151
152         virtual BOOL CanReach(UINT refSize, UINT variationCode, BOOL fExternal, INT_PTR offset)
153         {
154             if (fExternal) 
155             {
156                 // Note that the parameter 'offset' is not an offset but the target address itself (when fExternal is true)
157                 return (refSize == InstructionFormat::k64);
158             }
159             else
160             {
161                 return ((offset >= -134217728 && offset <= 134217724) || (refSize == InstructionFormat::k64));
162             }
163         }
164
165         virtual VOID EmitInstruction(UINT refSize, __int64 fixedUpReference, BYTE *pOutBuffer, UINT variationCode, BYTE *pDataBuffer)
166         {
167             LIMITED_METHOD_CONTRACT;
168
169             _ASSERTE(((UINT_PTR)pDataBuffer & 7) == 0);
170             __int64 dataOffset = pDataBuffer - pOutBuffer;
171
172             if (dataOffset < -1048576 || dataOffset > 1048572)
173                 COMPlusThrow(kNotSupportedException);
174
175             DWORD imm19 = (DWORD)(0x7FFFF & (dataOffset >> 2));
176
177             // +0: ldr x16, [pc, #dataOffset]
178             *((DWORD*)pOutBuffer) = (0x58000010 | (imm19 << 5));
179             DWORD offsetbranch = 0;
180
181             if (IsRelativeIndirect(variationCode))
182             {
183                 // TODO-ARM64-CQ: update this!
184                 // REG_IP1 (x17) is always reserved on arm64 (see Compiler::compRsvdRegCheck)
185                 // and is used as a temporary register. Use it as temp register here too.
186                 //
187                 // +4: mov x17, x16
188                 // +8: ldr x16, [x16]
189                 // +12: add x16, x16, x17
190                 *((DWORD*)(pOutBuffer+4)) = 0xAA1003F1;
191                 *((DWORD*)(pOutBuffer+8)) = 0xF9400210;
192                 *((DWORD*)(pOutBuffer+12)) = 0x8B110210;
193                 offsetbranch = 16;
194             }
195             else if (IsIndirect(variationCode))
196             {
197                 // +4: ldr x16, [x16]
198                 *((DWORD*)(pOutBuffer+4)) = 0xF9400210;
199                 offsetbranch = 8;
200             }
201             else
202             {
203                 offsetbranch = 4;
204             }
205
206             _ASSERTE(offsetbranch != 0);
207
208             // +offsetbranch: b(l)r x16
209             if (IsCall(variationCode))
210             {
211                 *((DWORD*)(pOutBuffer+offsetbranch)) = 0xD63F0200; // blr x16
212             }
213             else
214             {
215                 *((DWORD*)(pOutBuffer+offsetbranch)) = 0xD61F0200; // br x16
216             }
217
218             if (!ClrSafeInt<__int64>::addition(fixedUpReference, (__int64)pOutBuffer, fixedUpReference))
219                 COMPlusThrowArithmetic();
220             *((__int64*)pDataBuffer) = fixedUpReference;
221         }
222 };
223
224 //-----------------------------------------------------------------------
225 // InstructionFormat for loading a label to the register (ADRP/ADR)
226 //-----------------------------------------------------------------------
227 class LoadFromLabelInstructionFormat : public InstructionFormat
228 {
229     public:
230         LoadFromLabelInstructionFormat() : InstructionFormat( InstructionFormat::k32)
231         {
232             LIMITED_METHOD_CONTRACT;
233         }
234
235         virtual UINT GetSizeOfInstruction(UINT refSize, UINT variationCode)
236         {
237             WRAPPER_NO_CONTRACT;
238             return 8;
239
240         }
241
242         virtual UINT GetHotSpotOffset(UINT refsize, UINT variationCode)
243         {
244             WRAPPER_NO_CONTRACT;
245             return 0;
246         }
247
248         virtual BOOL CanReach(UINT refSize, UINT variationCode, BOOL fExternal, INT_PTR offset)
249         {
250             return fExternal;
251         }
252
253         virtual VOID EmitInstruction(UINT refSize, __int64 fixedUpReference, BYTE *pOutBuffer, UINT variationCode, BYTE *pDataBuffer)
254         {
255             LIMITED_METHOD_CONTRACT;
256             // VariationCode is used to indicate the register the label is going to be loaded
257                 
258             DWORD imm =(DWORD)(fixedUpReference>>12);
259             if (imm>>21)
260                 COMPlusThrow(kNotSupportedException);
261
262             // Can't use SP or XZR
263             _ASSERTE((variationCode & 0x1F) != 31);
264
265             // adrp Xt, #Page_of_fixedUpReference
266             *((DWORD*)pOutBuffer) = ((9<<28) | ((imm & 3)<<29) | (imm>>2)<<5 | (variationCode&0x1F));
267
268             // ldr Xt, [Xt, #offset_of_fixedUpReference_to_its_page]
269             UINT64 target = (UINT64)(fixedUpReference + pOutBuffer)>>3;
270             *((DWORD*)(pOutBuffer+4)) = ( 0xF9400000 | ((target & 0x1FF)<<10) | (variationCode & 0x1F)<<5 | (variationCode & 0x1F));
271         }
272 };
273
274
275
276 static BYTE gConditionalBranchIF[sizeof(ConditionalBranchInstructionFormat)];
277 static BYTE gBranchIF[sizeof(BranchInstructionFormat)];
278 static BYTE gLoadFromLabelIF[sizeof(LoadFromLabelInstructionFormat)];
279
280 #endif
281
282 void ClearRegDisplayArgumentAndScratchRegisters(REGDISPLAY * pRD)
283 {
284     for (int i=0; i < 18; i++)
285         pRD->volatileCurrContextPointers.X[i] = NULL;
286 }
287
288 #ifndef CROSSGEN_COMPILE
289 void LazyMachState::unwindLazyState(LazyMachState* baseState,
290                                     MachState* unwoundstate,
291                                     DWORD threadId,
292                                     int funCallDepth,
293                                     HostCallPreference hostCallPreference)
294 {
295     T_CONTEXT context;
296     T_KNONVOLATILE_CONTEXT_POINTERS nonVolContextPtrs;
297
298     context.X19 = unwoundstate->captureX19_X29[0] = baseState->captureX19_X29[0];
299     context.X20 = unwoundstate->captureX19_X29[1] = baseState->captureX19_X29[1];
300     context.X21 = unwoundstate->captureX19_X29[2] = baseState->captureX19_X29[2];
301     context.X22 = unwoundstate->captureX19_X29[3] = baseState->captureX19_X29[3];
302     context.X23 = unwoundstate->captureX19_X29[4] = baseState->captureX19_X29[4];
303     context.X24 = unwoundstate->captureX19_X29[5] = baseState->captureX19_X29[5];
304     context.X25 = unwoundstate->captureX19_X29[6] = baseState->captureX19_X29[6];
305     context.X26 = unwoundstate->captureX19_X29[7] = baseState->captureX19_X29[7];
306     context.X27 = unwoundstate->captureX19_X29[8] = baseState->captureX19_X29[8];
307     context.X28 = unwoundstate->captureX19_X29[9] = baseState->captureX19_X29[9];
308     context.Fp  = unwoundstate->captureX19_X29[10] = baseState->captureX19_X29[10];     
309     context.Lr = NULL; // Filled by the unwinder 
310
311     context.Sp = baseState->captureSp;
312     context.Pc = baseState->captureIp;
313
314 #if !defined(DACCESS_COMPILE)
315     // For DAC, if we get here, it means that the LazyMachState is uninitialized and we have to unwind it.
316     // The API we use to unwind in DAC is StackWalk64(), which does not support the context pointers.
317     //
318     // Restore the integer registers to KNONVOLATILE_CONTEXT_POINTERS to be used for unwinding.
319     nonVolContextPtrs.X19 = &unwoundstate->captureX19_X29[0];
320     nonVolContextPtrs.X20 = &unwoundstate->captureX19_X29[1];
321     nonVolContextPtrs.X21 = &unwoundstate->captureX19_X29[2];
322     nonVolContextPtrs.X22 = &unwoundstate->captureX19_X29[3];
323     nonVolContextPtrs.X23 = &unwoundstate->captureX19_X29[4];
324     nonVolContextPtrs.X24 = &unwoundstate->captureX19_X29[5];
325     nonVolContextPtrs.X25 = &unwoundstate->captureX19_X29[6];
326     nonVolContextPtrs.X26 = &unwoundstate->captureX19_X29[7];
327     nonVolContextPtrs.X27 = &unwoundstate->captureX19_X29[8];
328     nonVolContextPtrs.X28 = &unwoundstate->captureX19_X29[9];
329     nonVolContextPtrs.Fp  = &unwoundstate->captureX19_X29[10];  
330     nonVolContextPtrs.Lr = NULL; // Filled by the unwinder 
331
332 #endif // DACCESS_COMPILE
333
334     LOG((LF_GCROOTS, LL_INFO100000, "STACKWALK    LazyMachState::unwindLazyState(ip:%p,sp:%p)\n", baseState->captureIp, baseState->captureSp));
335
336     PCODE pvControlPc;
337
338     do {
339
340 #ifndef FEATURE_PAL
341         pvControlPc = Thread::VirtualUnwindCallFrame(&context, &nonVolContextPtrs);
342 #else // !FEATURE_PAL
343 #ifdef DACCESS_COMPILE
344         HRESULT hr = DacVirtualUnwind(threadId, &context, &nonVolContextPtrs);
345         if (FAILED(hr))
346         {
347             DacError(hr);
348         }
349 #else // DACCESS_COMPILE
350         BOOL success = PAL_VirtualUnwind(&context, &nonVolContextPtrs);
351         if (!success)
352         {
353             _ASSERTE(!"unwindLazyState: Unwinding failed");
354             EEPOLICY_HANDLE_FATAL_ERROR(COR_E_EXECUTIONENGINE);
355         }
356 #endif // DACCESS_COMPILE
357         pvControlPc = GetIP(&context);
358 #endif // !FEATURE_PAL
359
360         if (funCallDepth > 0)
361         {
362             funCallDepth--;
363             if (funCallDepth == 0)
364                 break;
365         }
366         else
367         {
368             // Determine  whether given IP resides in JITted code. (It returns nonzero in that case.) 
369             // Use it now to see if we've unwound to managed code yet.
370             BOOL fFailedReaderLock = FALSE;
371             BOOL fIsManagedCode = ExecutionManager::IsManagedCode(pvControlPc, hostCallPreference, &fFailedReaderLock);
372             if (fFailedReaderLock)
373             {
374                 // We don't know if we would have been able to find a JIT
375                 // manager, because we couldn't enter the reader lock without
376                 // yielding (and our caller doesn't want us to yield).  So abort
377                 // now.
378
379                 // Invalidate the lazyState we're returning, so the caller knows
380                 // we aborted before we could fully unwind
381                 unwoundstate->_isValid = false;
382                 return;
383             }
384
385             if (fIsManagedCode)
386                 break;
387
388         }
389     } while (true);
390
391 #ifdef FEATURE_PAL
392     unwoundstate->captureX19_X29[0] = context.X19;
393     unwoundstate->captureX19_X29[1] = context.X20;
394     unwoundstate->captureX19_X29[2] = context.X21;
395     unwoundstate->captureX19_X29[3] = context.X22;
396     unwoundstate->captureX19_X29[4] = context.X23;
397     unwoundstate->captureX19_X29[5] = context.X24;
398     unwoundstate->captureX19_X29[6] = context.X25;
399     unwoundstate->captureX19_X29[7] = context.X26;
400     unwoundstate->captureX19_X29[8] = context.X27;
401     unwoundstate->captureX19_X29[9] = context.X28;
402     unwoundstate->captureX19_X29[10] = context.Fp;
403 #endif
404
405 #ifdef DACCESS_COMPILE
406     // For DAC builds, we update the registers directly since we dont have context pointers
407     unwoundstate->captureX19_X29[0] = context.X19;
408     unwoundstate->captureX19_X29[1] = context.X20;
409     unwoundstate->captureX19_X29[2] = context.X21;
410     unwoundstate->captureX19_X29[3] = context.X22;
411     unwoundstate->captureX19_X29[4] = context.X23;
412     unwoundstate->captureX19_X29[5] = context.X24;
413     unwoundstate->captureX19_X29[6] = context.X25;
414     unwoundstate->captureX19_X29[7] = context.X26;
415     unwoundstate->captureX19_X29[8] = context.X27;
416     unwoundstate->captureX19_X29[9] = context.X28;
417     unwoundstate->captureX19_X29[10] = context.Fp;
418 #else // !DACCESS_COMPILE
419     // For non-DAC builds, update the register state from context pointers
420     unwoundstate->ptrX19_X29[0] = nonVolContextPtrs.X19;
421     unwoundstate->ptrX19_X29[1] = nonVolContextPtrs.X20;
422     unwoundstate->ptrX19_X29[2] = nonVolContextPtrs.X21;
423     unwoundstate->ptrX19_X29[3] = nonVolContextPtrs.X22;
424     unwoundstate->ptrX19_X29[4] = nonVolContextPtrs.X23;
425     unwoundstate->ptrX19_X29[5] = nonVolContextPtrs.X24;
426     unwoundstate->ptrX19_X29[6] = nonVolContextPtrs.X25;
427     unwoundstate->ptrX19_X29[7] = nonVolContextPtrs.X26;
428     unwoundstate->ptrX19_X29[8] = nonVolContextPtrs.X27;
429     unwoundstate->ptrX19_X29[9] = nonVolContextPtrs.X28;
430     unwoundstate->ptrX19_X29[10] = nonVolContextPtrs.Fp;        
431 #endif // DACCESS_COMPILE
432
433     unwoundstate->_pc = context.Pc;
434     unwoundstate->_sp = context.Sp;
435
436     unwoundstate->_isValid = TRUE;
437 }
438
439 void HelperMethodFrame::UpdateRegDisplay(const PREGDISPLAY pRD)
440 {
441     CONTRACTL
442     {
443         NOTHROW;
444         GC_NOTRIGGER;
445         MODE_ANY;
446         SUPPORTS_DAC;
447     }
448     CONTRACTL_END;
449     
450     pRD->IsCallerContextValid = FALSE;
451     pRD->IsCallerSPValid      = FALSE;        // Don't add usage of this field.  This is only temporary.
452     
453     //
454     // Copy the saved state from the frame to the current context.
455     //
456
457     LOG((LF_GCROOTS, LL_INFO100000, "STACKWALK    HelperMethodFrame::UpdateRegDisplay cached ip:%p, sp:%p\n", m_MachState._pc, m_MachState._sp));
458     
459  #if defined(DACCESS_COMPILE)
460     // For DAC, we may get here when the HMF is still uninitialized.
461     // So we may need to unwind here.
462     if (!m_MachState.isValid())
463     {
464         // This allocation throws on OOM.
465         MachState* pUnwoundState = (MachState*)DacAllocHostOnlyInstance(sizeof(*pUnwoundState), true);
466
467         InsureInit(false, pUnwoundState);
468
469         pRD->pCurrentContext->Pc = pRD->ControlPC = pUnwoundState->_pc;
470         pRD->pCurrentContext->Sp = pRD->SP        = pUnwoundState->_sp;
471
472         pRD->pCurrentContext->X19 = (DWORD64)(pUnwoundState->captureX19_X29[0]);
473         pRD->pCurrentContext->X20 = (DWORD64)(pUnwoundState->captureX19_X29[1]);
474         pRD->pCurrentContext->X21 = (DWORD64)(pUnwoundState->captureX19_X29[2]);
475         pRD->pCurrentContext->X22 = (DWORD64)(pUnwoundState->captureX19_X29[3]);
476         pRD->pCurrentContext->X23 = (DWORD64)(pUnwoundState->captureX19_X29[4]);
477         pRD->pCurrentContext->X24 = (DWORD64)(pUnwoundState->captureX19_X29[5]);
478         pRD->pCurrentContext->X25 = (DWORD64)(pUnwoundState->captureX19_X29[6]);
479         pRD->pCurrentContext->X26 = (DWORD64)(pUnwoundState->captureX19_X29[7]);
480         pRD->pCurrentContext->X27 = (DWORD64)(pUnwoundState->captureX19_X29[8]);
481         pRD->pCurrentContext->X28 = (DWORD64)(pUnwoundState->captureX19_X29[9]);
482         pRD->pCurrentContext->Fp = (DWORD64)(pUnwoundState->captureX19_X29[10]);
483         pRD->pCurrentContext->Lr = NULL; // Unwind again to get Caller's PC
484
485         pRD->pCurrentContextPointers->X19 = pUnwoundState->ptrX19_X29[0];
486         pRD->pCurrentContextPointers->X20 = pUnwoundState->ptrX19_X29[1];
487         pRD->pCurrentContextPointers->X21 = pUnwoundState->ptrX19_X29[2];
488         pRD->pCurrentContextPointers->X22 = pUnwoundState->ptrX19_X29[3];
489         pRD->pCurrentContextPointers->X23 = pUnwoundState->ptrX19_X29[4];
490         pRD->pCurrentContextPointers->X24 = pUnwoundState->ptrX19_X29[5];
491         pRD->pCurrentContextPointers->X25 = pUnwoundState->ptrX19_X29[6];
492         pRD->pCurrentContextPointers->X26 = pUnwoundState->ptrX19_X29[7];
493         pRD->pCurrentContextPointers->X27 = pUnwoundState->ptrX19_X29[8];
494         pRD->pCurrentContextPointers->X28 = pUnwoundState->ptrX19_X29[9];
495         pRD->pCurrentContextPointers->Fp = pUnwoundState->ptrX19_X29[10];
496         pRD->pCurrentContextPointers->Lr = NULL;
497
498         return;
499     }
500 #endif // DACCESS_COMPILE
501
502     // reset pContext; it's only valid for active (top-most) frame
503     pRD->pContext = NULL;
504     pRD->ControlPC = GetReturnAddress(); // m_MachState._pc;
505     pRD->SP = (DWORD64)(size_t)m_MachState._sp;
506     
507     pRD->pCurrentContext->Pc = pRD->ControlPC;
508     pRD->pCurrentContext->Sp = pRD->SP;
509
510 #ifdef FEATURE_PAL
511     pRD->pCurrentContext->X19 = m_MachState.ptrX19_X29[0] ? *m_MachState.ptrX19_X29[0] : m_MachState.captureX19_X29[0];
512     pRD->pCurrentContext->X20 = m_MachState.ptrX19_X29[1] ? *m_MachState.ptrX19_X29[1] : m_MachState.captureX19_X29[1];
513     pRD->pCurrentContext->X21 = m_MachState.ptrX19_X29[2] ? *m_MachState.ptrX19_X29[2] : m_MachState.captureX19_X29[2];
514     pRD->pCurrentContext->X22 = m_MachState.ptrX19_X29[3] ? *m_MachState.ptrX19_X29[3] : m_MachState.captureX19_X29[3];
515     pRD->pCurrentContext->X23 = m_MachState.ptrX19_X29[4] ? *m_MachState.ptrX19_X29[4] : m_MachState.captureX19_X29[4];
516     pRD->pCurrentContext->X24 = m_MachState.ptrX19_X29[5] ? *m_MachState.ptrX19_X29[5] : m_MachState.captureX19_X29[5];
517     pRD->pCurrentContext->X25 = m_MachState.ptrX19_X29[6] ? *m_MachState.ptrX19_X29[6] : m_MachState.captureX19_X29[6];
518     pRD->pCurrentContext->X26 = m_MachState.ptrX19_X29[7] ? *m_MachState.ptrX19_X29[7] : m_MachState.captureX19_X29[7];
519     pRD->pCurrentContext->X27 = m_MachState.ptrX19_X29[8] ? *m_MachState.ptrX19_X29[8] : m_MachState.captureX19_X29[8];
520     pRD->pCurrentContext->X28 = m_MachState.ptrX19_X29[9] ? *m_MachState.ptrX19_X29[9] : m_MachState.captureX19_X29[9];
521     pRD->pCurrentContext->Fp = m_MachState.ptrX19_X29[10] ? *m_MachState.ptrX19_X29[10] : m_MachState.captureX19_X29[10];
522     pRD->pCurrentContext->Lr = NULL; // Unwind again to get Caller's PC
523 #else // FEATURE_PAL
524     pRD->pCurrentContext->X19 = *m_MachState.ptrX19_X29[0];
525     pRD->pCurrentContext->X20 = *m_MachState.ptrX19_X29[1];
526     pRD->pCurrentContext->X21 = *m_MachState.ptrX19_X29[2];
527     pRD->pCurrentContext->X22 = *m_MachState.ptrX19_X29[3];
528     pRD->pCurrentContext->X23 = *m_MachState.ptrX19_X29[4];
529     pRD->pCurrentContext->X24 = *m_MachState.ptrX19_X29[5];
530     pRD->pCurrentContext->X25 = *m_MachState.ptrX19_X29[6];
531     pRD->pCurrentContext->X26 = *m_MachState.ptrX19_X29[7];
532     pRD->pCurrentContext->X27 = *m_MachState.ptrX19_X29[8];
533     pRD->pCurrentContext->X28 = *m_MachState.ptrX19_X29[9];
534     pRD->pCurrentContext->Fp  = *m_MachState.ptrX19_X29[10];
535     pRD->pCurrentContext->Lr = NULL; // Unwind again to get Caller's PC
536 #endif
537
538 #if !defined(DACCESS_COMPILE)    
539     pRD->pCurrentContextPointers->X19 = m_MachState.ptrX19_X29[0];
540     pRD->pCurrentContextPointers->X20 = m_MachState.ptrX19_X29[1];
541     pRD->pCurrentContextPointers->X21 = m_MachState.ptrX19_X29[2];
542     pRD->pCurrentContextPointers->X22 = m_MachState.ptrX19_X29[3];
543     pRD->pCurrentContextPointers->X23 = m_MachState.ptrX19_X29[4];
544     pRD->pCurrentContextPointers->X24 = m_MachState.ptrX19_X29[5];
545     pRD->pCurrentContextPointers->X25 = m_MachState.ptrX19_X29[6];
546     pRD->pCurrentContextPointers->X26 = m_MachState.ptrX19_X29[7];
547     pRD->pCurrentContextPointers->X27 = m_MachState.ptrX19_X29[8];
548     pRD->pCurrentContextPointers->X28 = m_MachState.ptrX19_X29[9];
549     pRD->pCurrentContextPointers->Fp = m_MachState.ptrX19_X29[10];
550     pRD->pCurrentContextPointers->Lr = NULL; // Unwind again to get Caller's PC
551 #endif
552
553     ClearRegDisplayArgumentAndScratchRegisters(pRD);
554 }
555 #endif // CROSSGEN_COMPILE
556
557 TADDR FixupPrecode::GetMethodDesc()
558 {
559     LIMITED_METHOD_DAC_CONTRACT;
560
561     // This lookup is also manually inlined in PrecodeFixupThunk assembly code
562     TADDR base = *PTR_TADDR(GetBase());
563     if (base == NULL)
564         return NULL;
565     return base + (m_MethodDescChunkIndex * MethodDesc::ALIGNMENT);
566 }
567
568 #ifdef DACCESS_COMPILE
569 void FixupPrecode::EnumMemoryRegions(CLRDataEnumMemoryFlags flags)
570 {
571         SUPPORTS_DAC;
572         DacEnumMemoryRegion(dac_cast<TADDR>(this), sizeof(FixupPrecode));
573
574         DacEnumMemoryRegion(GetBase(), sizeof(TADDR));
575 }
576 #endif // DACCESS_COMPILE
577
578 #ifndef DACCESS_COMPILE
579 void StubPrecode::Init(MethodDesc* pMD, LoaderAllocator *pLoaderAllocator)
580 {
581     WRAPPER_NO_CONTRACT;
582
583     int n = 0;
584
585     m_rgCode[n++] = 0x10000089; // adr x9, #16
586     m_rgCode[n++] = 0xA940312A; // ldp x10,x12,[x9] 
587     m_rgCode[n++] = 0xD61F0140; // br x10
588
589     _ASSERTE(n+1 == _countof(m_rgCode));
590
591     m_pTarget = GetPreStubEntryPoint();
592     m_pMethodDesc = (TADDR)pMD;
593 }
594
595 #ifdef FEATURE_NATIVE_IMAGE_GENERATION
596 void StubPrecode::Fixup(DataImage *image)
597 {
598     WRAPPER_NO_CONTRACT;
599
600     image->FixupFieldToNode(this, offsetof(StubPrecode, m_pTarget),
601                             image->GetHelperThunk(CORINFO_HELP_EE_PRESTUB),
602                             0,
603                             IMAGE_REL_BASED_PTR);
604
605     image->FixupField(this, offsetof(StubPrecode, m_pMethodDesc),
606                       (void*)GetMethodDesc(),
607                       0,
608                       IMAGE_REL_BASED_PTR);
609 }
610 #endif // FEATURE_NATIVE_IMAGE_GENERATION
611
612 void NDirectImportPrecode::Init(MethodDesc* pMD, LoaderAllocator *pLoaderAllocator)
613 {
614     WRAPPER_NO_CONTRACT;
615
616     int n = 0;
617
618     m_rgCode[n++] = 0x1000008B; // adr x11, #16
619     m_rgCode[n++] = 0xA940316A; // ldp x10,x12,[x11] 
620     m_rgCode[n++] = 0xD61F0140; // br x10
621
622     _ASSERTE(n+1 == _countof(m_rgCode));
623
624     m_pTarget = GetEEFuncEntryPoint(NDirectImportThunk);
625     m_pMethodDesc = (TADDR)pMD;
626 }
627
628 #ifdef FEATURE_NATIVE_IMAGE_GENERATION
629 void NDirectImportPrecode::Fixup(DataImage *image)
630 {
631     WRAPPER_NO_CONTRACT;
632
633     image->FixupField(this, offsetof(NDirectImportPrecode, m_pMethodDesc),
634                       (void*)GetMethodDesc(),
635                       0,
636                       IMAGE_REL_BASED_PTR);
637
638     image->FixupFieldToNode(this, offsetof(NDirectImportPrecode, m_pTarget),
639                             image->GetHelperThunk(CORINFO_HELP_EE_PINVOKE_FIXUP),
640                             0,
641                             IMAGE_REL_BASED_PTR);
642 }
643 #endif
644
645 void FixupPrecode::Init(MethodDesc* pMD, LoaderAllocator *pLoaderAllocator, int iMethodDescChunkIndex /*=0*/, int iPrecodeChunkIndex /*=0*/)
646 {
647     WRAPPER_NO_CONTRACT;
648     
649     InitCommon();
650
651     // Initialize chunk indices only if they are not initialized yet. This is necessary to make MethodDesc::Reset work.
652     if (m_PrecodeChunkIndex == 0)
653     {
654         _ASSERTE(FitsInU1(iPrecodeChunkIndex));
655         m_PrecodeChunkIndex = static_cast<BYTE>(iPrecodeChunkIndex);
656     }
657
658     if (iMethodDescChunkIndex != -1)
659     {
660         if (m_MethodDescChunkIndex == 0)
661         {
662             _ASSERTE(FitsInU1(iMethodDescChunkIndex));
663             m_MethodDescChunkIndex = static_cast<BYTE>(iMethodDescChunkIndex);
664         }
665
666         if (*(void**)GetBase() == NULL)
667             *(void**)GetBase() = (BYTE*)pMD - (iMethodDescChunkIndex * MethodDesc::ALIGNMENT);
668     }
669
670     _ASSERTE(GetMethodDesc() == (TADDR)pMD);
671
672     if (pLoaderAllocator != NULL)
673     {
674         m_pTarget = GetEEFuncEntryPoint(PrecodeFixupThunk);
675     }
676 }
677
678 #ifdef FEATURE_NATIVE_IMAGE_GENERATION
679 // Partial initialization. Used to save regrouped chunks.
680 void FixupPrecode::InitForSave(int iPrecodeChunkIndex)
681 {
682     STANDARD_VM_CONTRACT;
683
684     InitCommon();
685
686     _ASSERTE(FitsInU1(iPrecodeChunkIndex));
687     m_PrecodeChunkIndex = static_cast<BYTE>(iPrecodeChunkIndex);
688     // The rest is initialized in code:FixupPrecode::Fixup
689 }
690
691 void FixupPrecode::Fixup(DataImage *image, MethodDesc * pMD)
692 {
693     STANDARD_VM_CONTRACT;
694
695     // Note that GetMethodDesc() does not return the correct value because of 
696     // regrouping of MethodDescs into hot and cold blocks. That's why the caller
697     // has to supply the actual MethodDesc
698
699     SSIZE_T mdChunkOffset;
700     ZapNode * pMDChunkNode = image->GetNodeForStructure(pMD, &mdChunkOffset);
701     ZapNode * pHelperThunk = image->GetHelperThunk(CORINFO_HELP_EE_PRECODE_FIXUP);
702
703     image->FixupFieldToNode(this, offsetof(FixupPrecode, m_pTarget), pHelperThunk);
704
705     // Set the actual chunk index
706     FixupPrecode * pNewPrecode = (FixupPrecode *)image->GetImagePointer(this);
707
708     size_t mdOffset = mdChunkOffset - sizeof(MethodDescChunk);
709     size_t chunkIndex = mdOffset / MethodDesc::ALIGNMENT;
710     _ASSERTE(FitsInU1(chunkIndex));
711     pNewPrecode->m_MethodDescChunkIndex = (BYTE)chunkIndex;
712
713     // Fixup the base of MethodDescChunk
714     if (m_PrecodeChunkIndex == 0)
715     {
716         image->FixupFieldToNode(this, (BYTE *)GetBase() - (BYTE *)this,
717             pMDChunkNode, sizeof(MethodDescChunk));
718     }
719 }
720 #endif // FEATURE_NATIVE_IMAGE_GENERATION
721
722
723 void ThisPtrRetBufPrecode::Init(MethodDesc* pMD, LoaderAllocator *pLoaderAllocator)
724 {
725     WRAPPER_NO_CONTRACT;
726
727     int n = 0;
728     //Initially
729     //x0 -This ptr
730     //x1 -ReturnBuffer
731     m_rgCode[n++] = 0x91000010; // mov x16, x0
732     m_rgCode[n++] = 0x91000020; // mov x0, x1
733     m_rgCode[n++] = 0x91000201; // mov x1, x16
734     m_rgCode[n++] = 0x58000070; // ldr x16, [pc, #12]
735     _ASSERTE((UINT32*)&m_pTarget == &m_rgCode[n + 2]);
736     m_rgCode[n++] = 0xd61f0200; // br  x16
737     n++;                        // empty 4 bytes for data alignment below
738     _ASSERTE(n == _countof(m_rgCode));
739     
740     
741     m_pTarget = GetPreStubEntryPoint();
742     m_pMethodDesc = (TADDR)pMD;
743 }
744
745 #ifndef CROSSGEN_COMPILE
746 BOOL DoesSlotCallPrestub(PCODE pCode)
747 {
748     PTR_DWORD pInstr = dac_cast<PTR_DWORD>(PCODEToPINSTR(pCode));
749
750     //FixupPrecode
751 #if defined(HAS_FIXUP_PRECODE)
752     if (FixupPrecode::IsFixupPrecodeByASM(pCode))
753     {
754         PCODE pTarget = dac_cast<PTR_FixupPrecode>(pInstr)->m_pTarget;
755         
756         if (isJump(pTarget))
757         {
758             pTarget = decodeJump(pTarget);
759         }
760
761         return pTarget == (TADDR)PrecodeFixupThunk;
762     }
763 #endif
764
765     // StubPrecode
766     if (pInstr[0] == 0x10000089 && // adr x9, #16
767         pInstr[1] == 0xA940312A && // ldp x10,x12,[x9] 
768         pInstr[2] == 0xD61F0140) // br x10
769     {
770         PCODE pTarget = dac_cast<PTR_StubPrecode>(pInstr)->m_pTarget;
771
772         if (isJump(pTarget))
773         {
774             pTarget = decodeJump(pTarget);
775         }
776
777         return pTarget == GetPreStubEntryPoint();
778     }
779
780     return FALSE;
781
782 }
783
784 #endif // CROSSGEN_COMPILE
785
786 #endif // !DACCESS_COMPILE
787
788 void UpdateRegDisplayFromCalleeSavedRegisters(REGDISPLAY * pRD, CalleeSavedRegisters * pCalleeSaved)
789 {
790     LIMITED_METHOD_CONTRACT;
791
792     pRD->pCurrentContext->X19 = pCalleeSaved->x19;
793     pRD->pCurrentContext->X20 = pCalleeSaved->x20;
794     pRD->pCurrentContext->X21 = pCalleeSaved->x21;
795     pRD->pCurrentContext->X22 = pCalleeSaved->x22;
796     pRD->pCurrentContext->X23 = pCalleeSaved->x23;
797     pRD->pCurrentContext->X24 = pCalleeSaved->x24;
798     pRD->pCurrentContext->X25 = pCalleeSaved->x25;
799     pRD->pCurrentContext->X26 = pCalleeSaved->x26;
800     pRD->pCurrentContext->X27 = pCalleeSaved->x27;
801     pRD->pCurrentContext->X28 = pCalleeSaved->x28;
802     pRD->pCurrentContext->Fp  = pCalleeSaved->x29;
803     pRD->pCurrentContext->Lr  = pCalleeSaved->x30;
804
805     T_KNONVOLATILE_CONTEXT_POINTERS * pContextPointers = pRD->pCurrentContextPointers;
806     pContextPointers->X19 = (PDWORD64)&pCalleeSaved->x19;
807     pContextPointers->X20 = (PDWORD64)&pCalleeSaved->x20;
808     pContextPointers->X21 = (PDWORD64)&pCalleeSaved->x21;
809     pContextPointers->X22 = (PDWORD64)&pCalleeSaved->x22;
810     pContextPointers->X23 = (PDWORD64)&pCalleeSaved->x23;
811     pContextPointers->X24 = (PDWORD64)&pCalleeSaved->x24;
812     pContextPointers->X25 = (PDWORD64)&pCalleeSaved->x25;
813     pContextPointers->X26 = (PDWORD64)&pCalleeSaved->x26;
814     pContextPointers->X27 = (PDWORD64)&pCalleeSaved->x27;
815     pContextPointers->X28 = (PDWORD64)&pCalleeSaved->x28;
816     pContextPointers->Fp  = (PDWORD64)&pCalleeSaved->x29;
817     pContextPointers->Lr  = (PDWORD64)&pCalleeSaved->x30;
818 }
819
820 #ifndef CROSSGEN_COMPILE
821
822 void TransitionFrame::UpdateRegDisplay(const PREGDISPLAY pRD) 
823
824     pRD->IsCallerContextValid = FALSE;
825     pRD->IsCallerSPValid      = FALSE;        // Don't add usage of this field.  This is only temporary.
826     
827     // copy the callee saved regs
828     CalleeSavedRegisters *pCalleeSaved = GetCalleeSavedRegisters();
829     UpdateRegDisplayFromCalleeSavedRegisters(pRD, pCalleeSaved);
830
831     ClearRegDisplayArgumentAndScratchRegisters(pRD);
832
833     // copy the control registers
834     pRD->pCurrentContext->Fp = pCalleeSaved->x29;
835     pRD->pCurrentContext->Lr = pCalleeSaved->x30;
836     pRD->pCurrentContext->Pc = GetReturnAddress();
837     pRD->pCurrentContext->Sp = this->GetSP();
838
839     // Finally, syncup the regdisplay with the context
840     SyncRegDisplayToCurrentContext(pRD);
841     
842     LOG((LF_GCROOTS, LL_INFO100000, "STACKWALK    TransitionFrame::UpdateRegDisplay(pc:%p, sp:%p)\n", pRD->ControlPC, pRD->SP));
843 }
844
845
846 #endif
847
848 #ifndef CROSSGEN_COMPILE
849
850 void TailCallFrame::UpdateRegDisplay(const PREGDISPLAY pRD)
851
852     LOG((LF_GCROOTS, LL_INFO100000, "STACKWALK    TailCallFrame::UpdateRegDisplay(pc:%p, sp:%p)\n", pRD->ControlPC, pRD->SP));
853     _ASSERTE(!"ARM64:NYI");
854 }
855
856 #ifndef DACCESS_COMPILE
857 void TailCallFrame::InitFromContext(T_CONTEXT * pContext)
858 {
859     _ASSERTE(!"ARM64:NYI");
860 }
861 #endif // !DACCESS_COMPILE
862
863 #endif // CROSSGEN_COMPILE
864
865 void FaultingExceptionFrame::UpdateRegDisplay(const PREGDISPLAY pRD) 
866
867     LIMITED_METHOD_DAC_CONTRACT;
868
869     // Copy the context to regdisplay
870     memcpy(pRD->pCurrentContext, &m_ctx, sizeof(T_CONTEXT));
871
872     pRD->ControlPC = ::GetIP(&m_ctx);
873     pRD->SP = ::GetSP(&m_ctx);
874
875     // Update the integer registers in KNONVOLATILE_CONTEXT_POINTERS from
876     // the exception context we have.
877     pRD->pCurrentContextPointers->X19 = (PDWORD64)&m_ctx.X19;
878     pRD->pCurrentContextPointers->X20 = (PDWORD64)&m_ctx.X20;
879     pRD->pCurrentContextPointers->X21 = (PDWORD64)&m_ctx.X21;
880     pRD->pCurrentContextPointers->X22 = (PDWORD64)&m_ctx.X22;
881     pRD->pCurrentContextPointers->X23 = (PDWORD64)&m_ctx.X23;
882     pRD->pCurrentContextPointers->X24 = (PDWORD64)&m_ctx.X24;
883     pRD->pCurrentContextPointers->X25 = (PDWORD64)&m_ctx.X25;
884     pRD->pCurrentContextPointers->X26 = (PDWORD64)&m_ctx.X26;
885     pRD->pCurrentContextPointers->X27 = (PDWORD64)&m_ctx.X27;
886     pRD->pCurrentContextPointers->X28 = (PDWORD64)&m_ctx.X28;
887     pRD->pCurrentContextPointers->Fp = (PDWORD64)&m_ctx.Fp;
888     pRD->pCurrentContextPointers->Lr = (PDWORD64)&m_ctx.Lr;
889
890     ClearRegDisplayArgumentAndScratchRegisters(pRD);
891
892     pRD->IsCallerContextValid = FALSE;
893     pRD->IsCallerSPValid      = FALSE;        // Don't add usage of this field.  This is only temporary.
894
895     LOG((LF_GCROOTS, LL_INFO100000, "STACKWALK    FaultingExceptionFrame::UpdateRegDisplay(pc:%p, sp:%p)\n", pRD->ControlPC, pRD->SP));
896 }
897
898 void InlinedCallFrame::UpdateRegDisplay(const PREGDISPLAY pRD)
899 {
900     CONTRACT_VOID
901     {
902         NOTHROW;
903         GC_NOTRIGGER;
904 #ifdef PROFILING_SUPPORTED        
905         PRECONDITION(CORProfilerStackSnapshotEnabled() || InlinedCallFrame::FrameHasActiveCall(this));
906 #endif
907         HOST_NOCALLS;
908         MODE_ANY;
909         SUPPORTS_DAC;
910     }
911     CONTRACT_END;
912
913     if (!InlinedCallFrame::FrameHasActiveCall(this))
914     {
915         LOG((LF_CORDB, LL_ERROR, "WARNING: InlinedCallFrame::UpdateRegDisplay called on inactive frame %p\n", this));
916         return;
917     }
918
919     pRD->IsCallerContextValid = FALSE;
920     pRD->IsCallerSPValid      = FALSE;
921
922     pRD->pCurrentContext->Pc = *(DWORD64 *)&m_pCallerReturnAddress;
923     pRD->pCurrentContext->Sp = *(DWORD64 *)&m_pCallSiteSP;
924     pRD->pCurrentContext->Fp = *(DWORD64 *)&m_pCalleeSavedFP;
925
926     pRD->pCurrentContextPointers->X19 = NULL;
927     pRD->pCurrentContextPointers->X20 = NULL;
928     pRD->pCurrentContextPointers->X21 = NULL;
929     pRD->pCurrentContextPointers->X22 = NULL;
930     pRD->pCurrentContextPointers->X23 = NULL;
931     pRD->pCurrentContextPointers->X24 = NULL;
932     pRD->pCurrentContextPointers->X25 = NULL;
933     pRD->pCurrentContextPointers->X26 = NULL;
934     pRD->pCurrentContextPointers->X27 = NULL;
935     pRD->pCurrentContextPointers->X28 = NULL;
936
937     pRD->ControlPC = m_pCallerReturnAddress;
938     pRD->SP = (DWORD) dac_cast<TADDR>(m_pCallSiteSP);
939
940     // reset pContext; it's only valid for active (top-most) frame
941     pRD->pContext = NULL;
942
943     ClearRegDisplayArgumentAndScratchRegisters(pRD);
944
945
946     // Update the frame pointer in the current context.
947     pRD->pCurrentContextPointers->Fp = &m_pCalleeSavedFP;
948
949     LOG((LF_GCROOTS, LL_INFO100000, "STACKWALK    InlinedCallFrame::UpdateRegDisplay(pc:%p, sp:%p)\n", pRD->ControlPC, pRD->SP));
950
951     RETURN;
952 }
953
954 #ifdef FEATURE_HIJACK
955 TADDR ResumableFrame::GetReturnAddressPtr(void) 
956
957     LIMITED_METHOD_DAC_CONTRACT;
958     return dac_cast<TADDR>(m_Regs) + offsetof(T_CONTEXT, Pc);
959 }
960
961 void ResumableFrame::UpdateRegDisplay(const PREGDISPLAY pRD) 
962
963     CONTRACT_VOID
964     {
965         NOTHROW;
966         GC_NOTRIGGER;
967         MODE_ANY;
968         SUPPORTS_DAC;
969     }
970     CONTRACT_END;
971
972     CopyMemory(pRD->pCurrentContext, m_Regs, sizeof(T_CONTEXT));
973
974     pRD->ControlPC = m_Regs->Pc;
975     pRD->SP = m_Regs->Sp;
976
977     pRD->pCurrentContextPointers->X19 = &m_Regs->X19;
978     pRD->pCurrentContextPointers->X20 = &m_Regs->X20;
979     pRD->pCurrentContextPointers->X21 = &m_Regs->X21;
980     pRD->pCurrentContextPointers->X22 = &m_Regs->X22;
981     pRD->pCurrentContextPointers->X23 = &m_Regs->X23;
982     pRD->pCurrentContextPointers->X24 = &m_Regs->X24;
983     pRD->pCurrentContextPointers->X25 = &m_Regs->X25;
984     pRD->pCurrentContextPointers->X26 = &m_Regs->X26;
985     pRD->pCurrentContextPointers->X27 = &m_Regs->X27;
986     pRD->pCurrentContextPointers->X28 = &m_Regs->X28;
987     pRD->pCurrentContextPointers->Fp  = &m_Regs->Fp;
988     pRD->pCurrentContextPointers->Lr  = &m_Regs->Lr;
989
990     for (int i=0; i < 18; i++)
991         pRD->volatileCurrContextPointers.X[i] = &m_Regs->X[i];
992
993     pRD->IsCallerContextValid = FALSE;
994     pRD->IsCallerSPValid      = FALSE;        // Don't add usage of this field.  This is only temporary.
995     
996     LOG((LF_GCROOTS, LL_INFO100000, "STACKWALK    ResumableFrame::UpdateRegDisplay(pc:%p, sp:%p)\n", pRD->ControlPC, pRD->SP));
997
998     RETURN;
999 }
1000
1001 void HijackFrame::UpdateRegDisplay(const PREGDISPLAY pRD)
1002 {
1003     LIMITED_METHOD_CONTRACT;
1004
1005      pRD->IsCallerContextValid = FALSE;
1006      pRD->IsCallerSPValid      = FALSE;
1007  
1008      pRD->pCurrentContext->Pc = m_ReturnAddress;
1009      size_t s = sizeof(struct HijackArgs);
1010      _ASSERTE(s%8 == 0); // HijackArgs contains register values and hence will be a multiple of 8
1011      // stack must be multiple of 16. So if s is not multiple of 16 then there must be padding of 8 bytes 
1012      s = s + s%16; 
1013      pRD->pCurrentContext->Sp = PTR_TO_TADDR(m_Args) + s ;
1014  
1015      pRD->pCurrentContext->X0 = m_Args->X0;
1016
1017      pRD->pCurrentContext->X19 = m_Args->X19;
1018      pRD->pCurrentContext->X20 = m_Args->X20;
1019      pRD->pCurrentContext->X21 = m_Args->X21;
1020      pRD->pCurrentContext->X22 = m_Args->X22;
1021      pRD->pCurrentContext->X23 = m_Args->X23;
1022      pRD->pCurrentContext->X24 = m_Args->X24;
1023      pRD->pCurrentContext->X25 = m_Args->X25;
1024      pRD->pCurrentContext->X26 = m_Args->X26;
1025      pRD->pCurrentContext->X27 = m_Args->X27;
1026      pRD->pCurrentContext->X28 = m_Args->X28;
1027      pRD->pCurrentContext->Fp = m_Args->X29;
1028      pRD->pCurrentContext->Lr = m_Args->Lr;
1029
1030      pRD->pCurrentContextPointers->X19 = &m_Args->X19;
1031      pRD->pCurrentContextPointers->X20 = &m_Args->X20;
1032      pRD->pCurrentContextPointers->X21 = &m_Args->X21;
1033      pRD->pCurrentContextPointers->X22 = &m_Args->X22;
1034      pRD->pCurrentContextPointers->X23 = &m_Args->X23;
1035      pRD->pCurrentContextPointers->X24 = &m_Args->X24;
1036      pRD->pCurrentContextPointers->X25 = &m_Args->X25;
1037      pRD->pCurrentContextPointers->X26 = &m_Args->X26;
1038      pRD->pCurrentContextPointers->X27 = &m_Args->X27;
1039      pRD->pCurrentContextPointers->X28 = &m_Args->X28;
1040      pRD->pCurrentContextPointers->Fp = &m_Args->X29;
1041      pRD->pCurrentContextPointers->Lr = NULL;
1042
1043      SyncRegDisplayToCurrentContext(pRD);
1044
1045     LOG((LF_GCROOTS, LL_INFO100000, "STACKWALK    HijackFrame::UpdateRegDisplay(pc:%p, sp:%p)\n", pRD->ControlPC, pRD->SP));
1046 }
1047 #endif // FEATURE_HIJACK
1048
1049 #ifdef FEATURE_COMINTEROP
1050
1051 void emitCOMStubCall (ComCallMethodDesc *pCOMMethod, PCODE target)
1052 {
1053     WRAPPER_NO_CONTRACT;
1054
1055         // adr x12, label_comCallMethodDesc
1056         // ldr x10, label_target
1057         // br x10
1058         // 4 byte padding for alignment
1059         // label_target:
1060     // target address (8 bytes)
1061     // label_comCallMethodDesc:
1062     DWORD rgCode[] = {
1063         0x100000cc,
1064         0x5800006a,
1065         0xd61f0140
1066     };
1067
1068     BYTE *pBuffer = (BYTE*)pCOMMethod - COMMETHOD_CALL_PRESTUB_SIZE;
1069
1070     memcpy(pBuffer, rgCode, sizeof(rgCode));
1071     *((PCODE*)(pBuffer + sizeof(rgCode) + 4)) = target;
1072
1073     // Ensure that the updated instructions get actually written
1074     ClrFlushInstructionCache(pBuffer, COMMETHOD_CALL_PRESTUB_SIZE);
1075
1076     _ASSERTE(IS_ALIGNED(pBuffer + COMMETHOD_CALL_PRESTUB_ADDRESS_OFFSET, sizeof(void*)) &&
1077              *((PCODE*)(pBuffer + COMMETHOD_CALL_PRESTUB_ADDRESS_OFFSET)) == target);
1078 }
1079 #endif // FEATURE_COMINTEROP
1080
1081 void JIT_TailCall() 
1082 {
1083     _ASSERTE(!"ARM64:NYI");
1084 }
1085
1086 #if !defined(DACCESS_COMPILE) && !defined(CROSSGEN_COMPILE)
1087 void InitJITHelpers1()
1088 {
1089     STANDARD_VM_CONTRACT;
1090
1091     _ASSERTE(g_SystemInfo.dwNumberOfProcessors != 0);
1092
1093     // Allocation helpers, faster but non-logging
1094     if (!((TrackAllocationsEnabled()) ||
1095         (LoggingOn(LF_GCALLOC, LL_INFO10))
1096 #ifdef _DEBUG
1097         || (g_pConfig->ShouldInjectFault(INJECTFAULT_GCHEAP) != 0)
1098 #endif // _DEBUG
1099         ))
1100     {
1101         if (GCHeapUtilities::UseThreadAllocationContexts())
1102         {
1103             SetJitHelperFunction(CORINFO_HELP_NEWSFAST, JIT_NewS_MP_FastPortable);
1104             SetJitHelperFunction(CORINFO_HELP_NEWSFAST_ALIGN8, JIT_NewS_MP_FastPortable);
1105             SetJitHelperFunction(CORINFO_HELP_NEWARR_1_VC, JIT_NewArr1VC_MP_FastPortable);
1106             SetJitHelperFunction(CORINFO_HELP_NEWARR_1_OBJ, JIT_NewArr1OBJ_MP_FastPortable);
1107
1108             ECall::DynamicallyAssignFCallImpl(GetEEFuncEntryPoint(AllocateString_MP_FastPortable), ECall::FastAllocateString);
1109         }
1110     }
1111
1112     JIT_UpdateWriteBarrierState(GCHeapUtilities::IsServerHeap());
1113 }
1114
1115 #else
1116 EXTERN_C void JIT_UpdateWriteBarrierState(bool) {}
1117 #endif // !defined(DACCESS_COMPILE) && !defined(CROSSGEN_COMPILE)
1118
1119 PTR_CONTEXT GetCONTEXTFromRedirectedStubStackFrame(T_DISPATCHER_CONTEXT * pDispatcherContext)
1120 {
1121     LIMITED_METHOD_DAC_CONTRACT;
1122
1123     DWORD64 stackSlot = pDispatcherContext->EstablisherFrame + REDIRECTSTUB_SP_OFFSET_CONTEXT;
1124     PTR_PTR_CONTEXT ppContext = dac_cast<PTR_PTR_CONTEXT>((TADDR)stackSlot);
1125     return *ppContext;
1126 }
1127
1128 PTR_CONTEXT GetCONTEXTFromRedirectedStubStackFrame(T_CONTEXT * pContext)
1129 {
1130     LIMITED_METHOD_DAC_CONTRACT;
1131
1132     DWORD64 stackSlot = pContext->Sp + REDIRECTSTUB_SP_OFFSET_CONTEXT;
1133     PTR_PTR_CONTEXT ppContext = dac_cast<PTR_PTR_CONTEXT>((TADDR)stackSlot);
1134     return *ppContext;
1135 }
1136
1137 void RedirectForThreadAbort()
1138 {
1139     // ThreadAbort is not supported in .net core
1140     throw "NYI";
1141 }
1142
1143 #if !defined(DACCESS_COMPILE) && !defined (CROSSGEN_COMPILE)
1144 FaultingExceptionFrame *GetFrameFromRedirectedStubStackFrame (DISPATCHER_CONTEXT *pDispatcherContext)
1145 {
1146     LIMITED_METHOD_CONTRACT;
1147
1148     return (FaultingExceptionFrame*)((TADDR)pDispatcherContext->ContextRecord->X19);
1149 }
1150
1151
1152 BOOL
1153 AdjustContextForVirtualStub(
1154         EXCEPTION_RECORD *pExceptionRecord,
1155         CONTEXT *pContext)
1156 {
1157     LIMITED_METHOD_CONTRACT;
1158
1159     Thread * pThread = GetThread();
1160
1161     // We may not have a managed thread object. Example is an AV on the helper thread.
1162     // (perhaps during StubManager::IsStub)
1163     if (pThread == NULL)
1164     {
1165         return FALSE;
1166     }
1167
1168     PCODE f_IP = GetIP(pContext);
1169
1170     VirtualCallStubManager::StubKind sk;
1171     VirtualCallStubManager::FindStubManager(f_IP, &sk);
1172
1173     if (sk == VirtualCallStubManager::SK_DISPATCH)
1174     {
1175         if (*PTR_DWORD(f_IP) != DISPATCH_STUB_FIRST_DWORD)
1176         {
1177             _ASSERTE(!"AV in DispatchStub at unknown instruction");
1178             return FALSE;
1179         }
1180     }
1181     else
1182     if (sk == VirtualCallStubManager::SK_RESOLVE)
1183     {
1184         if (*PTR_DWORD(f_IP) != RESOLVE_STUB_FIRST_DWORD)
1185         {
1186             _ASSERTE(!"AV in ResolveStub at unknown instruction");
1187             return FALSE;
1188         }
1189     }
1190     else
1191     {
1192         return FALSE;
1193     }
1194
1195     PCODE callsite = GetAdjustedCallAddress(GetLR(pContext)); 
1196
1197     // Lr must already have been saved before calling so it should not be necessary to restore Lr
1198
1199     pExceptionRecord->ExceptionAddress = (PVOID)callsite;
1200     SetIP(pContext, callsite);
1201
1202     return TRUE;
1203 }
1204 #endif // !(DACCESS_COMPILE && CROSSGEN_COMPILE)
1205
1206 UMEntryThunk * UMEntryThunk::Decode(void *pCallback)
1207 {
1208     _ASSERTE(offsetof(UMEntryThunkCode, m_code) == 0);
1209     UMEntryThunkCode * pCode = (UMEntryThunkCode*)pCallback;
1210
1211     // We may be called with an unmanaged external code pointer instead. So if it doesn't look like one of our
1212     // stubs (see UMEntryThunkCode::Encode below) then we'll return NULL. Luckily in these scenarios our
1213     // caller will perform a hash lookup on successful return to verify our result in case random unmanaged
1214     // code happens to look like ours.
1215     if ((pCode->m_code[0] == 0x1000008c) &&
1216         (pCode->m_code[1] == 0xa9403190) &&
1217         (pCode->m_code[2] == 0xd61f0200))
1218     {
1219         return (UMEntryThunk*)pCode->m_pvSecretParam;
1220     }
1221
1222     return NULL;
1223 }
1224
1225 void UMEntryThunkCode::Encode(BYTE* pTargetCode, void* pvSecretParam)
1226 {
1227     // adr x12, _label
1228     // ldp x16, x12, [x12]
1229     // br x16
1230     // 4bytes padding
1231     // _label
1232     // m_pTargetCode data
1233     // m_pvSecretParam data
1234     
1235     m_code[0] = 0x1000008c;
1236     m_code[1] = 0xa9403190;
1237     m_code[2] = 0xd61f0200;
1238
1239
1240     m_pTargetCode = (TADDR)pTargetCode;
1241     m_pvSecretParam = (TADDR)pvSecretParam;
1242
1243     FlushInstructionCache(GetCurrentProcess(),&m_code,sizeof(m_code));
1244 }
1245
1246 #ifndef DACCESS_COMPILE
1247
1248 void UMEntryThunkCode::Poison()
1249 {
1250     m_pTargetCode = (TADDR)UMEntryThunk::ReportViolation;
1251
1252     // ldp x16, x0, [x12]
1253     m_code[1] = 0xa9400190;
1254
1255     ClrFlushInstructionCache(&m_code,sizeof(m_code));
1256 }
1257
1258 #endif // DACCESS_COMPILE
1259
1260 #if !defined(DACCESS_COMPILE)
1261 VOID ResetCurrentContext()
1262 {
1263     LIMITED_METHOD_CONTRACT;
1264 }
1265 #endif
1266
1267 LONG CLRNoCatchHandler(EXCEPTION_POINTERS* pExceptionInfo, PVOID pv)
1268 {
1269     return EXCEPTION_CONTINUE_SEARCH;
1270 }
1271
1272 void FlushWriteBarrierInstructionCache()
1273 {
1274     // this wouldn't be called in arm64, just to comply with gchelpers.h
1275 }
1276
1277 #ifndef CROSSGEN_COMPILE
1278 int StompWriteBarrierEphemeral(bool isRuntimeSuspended)
1279 {
1280     JIT_UpdateWriteBarrierState(GCHeapUtilities::IsServerHeap());
1281     return SWB_PASS;
1282 }
1283
1284 int StompWriteBarrierResize(bool isRuntimeSuspended, bool bReqUpperBoundsCheck)
1285 {
1286     JIT_UpdateWriteBarrierState(GCHeapUtilities::IsServerHeap());
1287     return SWB_PASS;
1288 }
1289
1290 #ifdef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
1291 int SwitchToWriteWatchBarrier(bool isRuntimeSuspended)
1292 {
1293     JIT_UpdateWriteBarrierState(GCHeapUtilities::IsServerHeap());
1294     return SWB_PASS;
1295 }
1296
1297 int SwitchToNonWriteWatchBarrier(bool isRuntimeSuspended)
1298 {
1299     JIT_UpdateWriteBarrierState(GCHeapUtilities::IsServerHeap());
1300     return SWB_PASS;
1301 }
1302 #endif // FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
1303 #endif // CROSSGEN_COMPILE
1304
1305 #ifdef DACCESS_COMPILE
1306 BOOL GetAnyThunkTarget (T_CONTEXT *pctx, TADDR *pTarget, TADDR *pTargetMethodDesc)
1307 {
1308     _ASSERTE(!"ARM64:NYI");
1309     return FALSE;
1310 }
1311 #endif // DACCESS_COMPILE
1312
1313 #ifndef DACCESS_COMPILE
1314 // ----------------------------------------------------------------
1315 // StubLinkerCPU methods
1316 // ----------------------------------------------------------------
1317
1318 void StubLinkerCPU::EmitMovConstant(IntReg target, UINT64 constant)
1319 {
1320 #define WORD_MASK 0xFFFF
1321
1322         // Move the 64bit constant in 4 chunks (of 16 bits).
1323         // MOVZ Rd, <1st word>, LSL 0
1324         // MOVK Rd, <2nd word>, LSL 1
1325         // MOVK Rd, <3nd word>, LSL 2
1326         // MOVK Rd, <4nd word>, LSL 3
1327         WORD word = (WORD) (constant & WORD_MASK);
1328         Emit32((DWORD)(0xD2<<24 | (4)<<21 | word<<5 | target));
1329         if (!(constant & 0xFFFF)) return;
1330
1331         word = (WORD) ((constant>>16) & WORD_MASK);
1332         if (word != 0)
1333             Emit32((DWORD)(0xF2<<24 | (5)<<21 | word<<5 | target));
1334         if (!(constant & 0xFFFFFFFF)) return;
1335
1336         word = (WORD) ((constant>>32) & WORD_MASK);
1337         if (word != 0)
1338             Emit32((DWORD)(0xF2<<24 | (6)<<21 | word<<5 | target));
1339         if (!(constant & 0xFFFFFFFFFFFF)) return;
1340
1341         word = (WORD) ((constant>>48) & WORD_MASK);
1342         if (word != 0)
1343             Emit32((DWORD)(0xF2<<24 | (7)<<21 | word<<5 | target));
1344 #undef WORD_MASK
1345 }
1346
1347 void StubLinkerCPU::EmitCmpImm(IntReg reg, int imm)
1348 {
1349
1350     if (0 <= imm && imm < 4096)
1351     {
1352         // CMP <Xn|SP>, #<imm>{, <shift>}
1353         // Encoding: 1|1|1|1|0|0|0|0|shift(2)|imm(12)|Rn|Rt
1354         // Where I encode shift as 0 and Rt has to be 1F
1355         Emit32((DWORD) ((0xF1<<24) | ((0xFFF & imm)<<10) | (reg<<5) | (0x1F)) );
1356         
1357     }
1358     else
1359         _ASSERTE(!"ARM64: NYI");
1360 }
1361
1362 void StubLinkerCPU::EmitCmpReg(IntReg Xn, IntReg Xm)
1363 {
1364
1365     // Encoding for CMP (shifted register)
1366     // sf|1|1|0|1|0|1|1|shift(2)|0|Xm(5)|imm(6)|Xn(5)|XZR(5)
1367     // where 
1368     //    sf = 1 for 64-bit variant,
1369     //    shift will be set to 00 (LSL)
1370     //    imm(6), which is the shift amount, will be set to 0 
1371     
1372     Emit32((DWORD) (0xEB<<24) | (Xm<<16) | (Xn<<5) | 0x1F);
1373 }
1374
1375 void StubLinkerCPU::EmitCondFlagJump(CodeLabel * target, UINT cond)
1376 {
1377     WRAPPER_NO_CONTRACT;
1378     EmitLabelRef(target, reinterpret_cast<ConditionalBranchInstructionFormat&>(gConditionalBranchIF), cond);
1379 }
1380
1381 void StubLinkerCPU::EmitJumpRegister(IntReg regTarget)
1382 {
1383     // br regTarget
1384     Emit32((DWORD) (0x3587C0<<10 | regTarget<<5));
1385 }
1386
1387 void StubLinkerCPU::EmitProlog(unsigned short cIntRegArgs, unsigned short cVecRegArgs, unsigned short cCalleeSavedRegs, unsigned short cbStackSpace)
1388 {
1389
1390     _ASSERTE(!m_fProlog);
1391
1392     unsigned short numberOfEntriesOnStack  = 2 + cIntRegArgs + cVecRegArgs + cCalleeSavedRegs; // 2 for fp, lr
1393
1394     // Stack needs to be 16 byte (2 qword) aligned. Compute the required padding before saving it
1395     unsigned short totalPaddedFrameSize = static_cast<unsigned short>(ALIGN_UP(cbStackSpace + numberOfEntriesOnStack *sizeof(void*), 2*sizeof(void*)));
1396     // The padding is going to be applied to the local stack
1397     cbStackSpace =  totalPaddedFrameSize - numberOfEntriesOnStack *sizeof(void*);
1398
1399     // Record the parameters of this prolog so that we can generate a matching epilog and unwind info.
1400     DescribeProlog(cIntRegArgs, cVecRegArgs, cCalleeSavedRegs, cbStackSpace);
1401
1402
1403     
1404     // N.B Despite the range of a jump with a sub sp is 4KB, we're limiting to 504 to save from emiting right prolog that's 
1405     // expressable in unwind codes efficiently. The largest offset in typical unwindinfo encodings that we use is 504. 
1406     // so allocations larger than 504 bytes would require setting the SP in multiple strides, which would complicate both
1407     // prolog and epilog generation as well as unwindinfo generation. 
1408     _ASSERTE((totalPaddedFrameSize <= 504) && "NYI:ARM64 Implement StubLinker prologs with larger than 504 bytes of frame size");
1409     if (totalPaddedFrameSize > 504)
1410         COMPlusThrow(kNotSupportedException);
1411     
1412     // Here is how the stack would look like (Stack grows up)
1413     // [Low Address]
1414     //            +------------+
1415     //      SP -> |            | <-+
1416     //            :            :   | Stack Frame, (i.e outgoing arguments) including padding
1417     //            |            | <-+
1418     //            +------------+
1419     //            | FP         | 
1420     //            +------------+
1421     //            | LR         | 
1422     //            +------------+
1423     //            | X19        | <-+
1424     //            +------------+   |
1425     //            :            :   | Callee-saved registers 
1426     //            +------------+   |
1427     //            | X28        | <-+
1428     //            +------------+ 
1429     //            | V0         | <-+
1430     //            +------------+   |
1431     //            :            :   | Vec Args
1432     //            +------------+   |
1433     //            | V7         | <-+
1434     //            +------------+ 
1435     //            | X0         | <-+
1436     //            +------------+   |
1437     //            :            :   | Int Args
1438     //            +------------+   |
1439     //            | X7         | <-+
1440     //            +------------+
1441     //  Old SP -> |[Stack Args]|
1442     // [High Address]
1443     
1444
1445
1446     // Regarding the order of operations in the prolog and epilog;
1447     // If the prolog and the epilog matches each other we can simplify emitting the unwind codes and save a few
1448     // bytes of unwind codes by making prolog and epilog share the same unwind codes.
1449     // In order to do that we need to make the epilog be the reverse of the prolog.
1450     // But we wouldn't want to add restoring of the argument registers as that's completely unnecessary.
1451     // Besides, saving argument registers cannot be expressed by the unwind code encodings.
1452     // So, we'll push saving the argument registers to the very last in the prolog, skip restoring it in epilog,
1453     // and also skip reporting it to the OS.
1454     //
1455     // Another bit that we can save is resetting the frame pointer.
1456     // This is not necessary when the SP doesn't get modified beyond prolog and epilog. (i.e no alloca/localloc)
1457     // And in that case we don't need to report setting up the FP either. 
1458     
1459     
1460
1461     // 1. Relocate SP
1462     EmitSubImm(RegSp, RegSp, totalPaddedFrameSize);
1463
1464     unsigned cbOffset = 2*sizeof(void*) + cbStackSpace; // 2 is for fp,lr
1465
1466     // 2. Store callee-saved registers
1467     _ASSERTE(cCalleeSavedRegs <= 10);
1468     for (unsigned short i=0; i<(cCalleeSavedRegs/2)*2; i+=2)
1469         EmitLoadStoreRegPairImm(eSTORE, IntReg(19+i), IntReg(19+i+1), RegSp, cbOffset + i*sizeof(void*));
1470     if ((cCalleeSavedRegs %2) ==1)
1471         EmitLoadStoreRegImm(eSTORE, IntReg(cCalleeSavedRegs-1), RegSp, cbOffset + (cCalleeSavedRegs-1)*sizeof(void*));
1472
1473     // 3. Store FP/LR
1474     EmitLoadStoreRegPairImm(eSTORE, RegFp, RegLr, RegSp, cbStackSpace);
1475
1476     // 4. Set the frame pointer
1477     EmitMovReg(RegFp, RegSp);
1478     
1479     // 5. Store floating point argument registers
1480     cbOffset += cCalleeSavedRegs*sizeof(void*);
1481     _ASSERTE(cVecRegArgs <= 8);
1482     for (unsigned short i=0; i<(cVecRegArgs/2)*2; i+=2)
1483         EmitLoadStoreRegPairImm(eSTORE, VecReg(i), VecReg(i+1), RegSp, cbOffset + i*sizeof(void*));
1484     if ((cVecRegArgs % 2) == 1)
1485         EmitLoadStoreRegImm(eSTORE, VecReg(cVecRegArgs-1), RegSp, cbOffset + (cVecRegArgs-1)*sizeof(void*));
1486
1487     // 6. Store int argument registers
1488     cbOffset += cVecRegArgs*sizeof(void*);
1489     _ASSERTE(cIntRegArgs <= 8);
1490     for (unsigned short i=0 ; i<(cIntRegArgs/2)*2; i+=2)
1491         EmitLoadStoreRegPairImm(eSTORE, IntReg(i), IntReg(i+1), RegSp, cbOffset + i*sizeof(void*));
1492     if ((cIntRegArgs % 2) == 1)
1493         EmitLoadStoreRegImm(eSTORE,IntReg(cIntRegArgs-1), RegSp, cbOffset + (cIntRegArgs-1)*sizeof(void*));
1494 }
1495
1496 void StubLinkerCPU::EmitEpilog()
1497 {
1498     _ASSERTE(m_fProlog);
1499
1500     // 6. Restore int argument registers
1501     //    nop: We don't need to. They are scratch registers
1502
1503     // 5. Restore floating point argument registers
1504     //    nop: We don't need to. They are scratch registers
1505
1506     // 4. Restore the SP from FP
1507     //    N.B. We're assuming that the stublinker stubs doesn't do alloca, hence nop
1508
1509     // 3. Restore FP/LR
1510     EmitLoadStoreRegPairImm(eLOAD, RegFp, RegLr, RegSp, m_cbStackSpace);
1511
1512     // 2. restore the calleeSavedRegisters
1513     unsigned cbOffset = 2*sizeof(void*) + m_cbStackSpace; // 2 is for fp,lr
1514     if ((m_cCalleeSavedRegs %2) ==1)
1515         EmitLoadStoreRegImm(eLOAD, IntReg(m_cCalleeSavedRegs-1), RegSp, cbOffset + (m_cCalleeSavedRegs-1)*sizeof(void*));
1516     for (int i=(m_cCalleeSavedRegs/2)*2-2; i>=0; i-=2)
1517         EmitLoadStoreRegPairImm(eLOAD, IntReg(19+i), IntReg(19+i+1), RegSp, cbOffset + i*sizeof(void*));
1518
1519     // 1. Restore SP
1520     EmitAddImm(RegSp, RegSp, GetStackFrameSize());
1521     EmitRet(RegLr);
1522 }
1523
1524 void StubLinkerCPU::EmitRet(IntReg Xn)
1525 {
1526     // Encoding: 1101011001011111000000| Rn |00000
1527     Emit32((DWORD)(0xD65F0000 | (Xn << 5)));
1528 }
1529
1530 void StubLinkerCPU::EmitLoadStoreRegPairImm(DWORD flags, IntReg Xt1, IntReg Xt2, IntReg Xn, int offset)
1531 {
1532     EmitLoadStoreRegPairImm(flags, (int)Xt1, (int)Xt2, Xn, offset, FALSE);
1533 }
1534
1535 void StubLinkerCPU::EmitLoadStoreRegPairImm(DWORD flags, VecReg Vt1, VecReg Vt2, IntReg Xn, int offset)
1536 {
1537     EmitLoadStoreRegPairImm(flags, (int)Vt1, (int)Vt2, Xn, offset, TRUE);
1538 }
1539
1540 void StubLinkerCPU::EmitLoadStoreRegPairImm(DWORD flags, int regNum1, int regNum2, IntReg Xn, int offset, BOOL isVec)
1541 {
1542     // Encoding:
1543     // [opc(2)] | 1 | 0 | 1 | [IsVec(1)] | 0 | [!postIndex(1)] | [writeBack(1)] | [isLoad(1)] | [imm(7)] | [Xt2(5)] | [Xn(5)] | [Xt1(5)]
1544     // where opc=01 and if isVec==1, opc=10 otherwise
1545
1546     BOOL isLoad    = flags & 1;
1547     BOOL writeBack = flags & 2;
1548     BOOL postIndex = flags & 4;
1549     _ASSERTE((-512 <= offset) && (offset <= 504));
1550     _ASSERTE((offset & 7) == 0);
1551     int opc = isVec ? 1 : 2;
1552     Emit32((DWORD) ( (opc<<30) | // opc
1553                      (0x5<<27) |
1554                      (!!isVec<<26) | 
1555                      (!postIndex<<24) |
1556                      (!!writeBack<<23) |
1557                      (!!isLoad<<22) |
1558                      ((0x7F & (offset >> 3)) << 15) |
1559                      (regNum2 << 10) |
1560                      (Xn << 5) |
1561                      (regNum1)
1562                    ));
1563
1564 }
1565
1566
1567 void StubLinkerCPU::EmitLoadStoreRegImm(DWORD flags, IntReg Xt, IntReg Xn, int offset)
1568 {
1569     EmitLoadStoreRegImm(flags, (int)Xt, Xn, offset, FALSE);
1570 }
1571 void StubLinkerCPU::EmitLoadStoreRegImm(DWORD flags, VecReg Vt, IntReg Xn, int offset)
1572 {
1573     EmitLoadStoreRegImm(flags, (int)Vt, Xn, offset, TRUE);
1574 }
1575
1576 void StubLinkerCPU::EmitLoadStoreRegImm(DWORD flags, int regNum, IntReg Xn, int offset, BOOL isVec)
1577 {
1578     // Encoding:
1579     // wb=1 : [size(2)=11] | 1 | 1 | 1 | [IsVec(1)] | 0 | [!writeBack(1)] | 0 | [isLoad(1)] | 0 | [imm(7)] | [!postIndex(1)] | [Xn(5)] | [Xt(5)]
1580     // wb=0 : [size(2)=11] | 1 | 1 | 1 | [IsVec(1)] | 0 | [!writeBack(1)] | 0 | [isLoad(1)] | [          imm(12)           ] | [Xn(5)] | [Xt(5)]
1581     // where IsVec=0 for IntReg, 1 for VecReg
1582
1583     BOOL isLoad    = flags & 1;
1584     BOOL writeBack = flags & 2;
1585     BOOL postIndex = flags & 4;
1586     if (writeBack) 
1587     {
1588         _ASSERTE(-256 <= offset && offset <= 255);
1589         Emit32((DWORD) ( (0x1F<<27) |
1590                          (!!isVec<<26) |
1591                          (!writeBack<<24) | 
1592                          (!!isLoad<<22) |
1593                          ((0x1FF & offset) << 12) |
1594                          (!postIndex<<11) |
1595                          (0x1<<10) |
1596                          (Xn<<5) |
1597                          (regNum))
1598               );
1599     }
1600     else
1601     {
1602         _ASSERTE((0 <= offset) && (offset <= 32760));
1603         _ASSERTE((offset & 7) == 0);
1604         Emit32((DWORD) ( (0x1F<<27) |
1605                          (!!isVec<<26) | 
1606                          (!writeBack<<24) |
1607                          (!!isLoad<<22) |
1608                          ((0xFFF & (offset >> 3)) << 10) |
1609                          (Xn<<5) |
1610                          (regNum))
1611               );
1612     }
1613
1614               
1615
1616 }
1617
1618 // Load Register (Register Offset)
1619 void StubLinkerCPU::EmitLoadRegReg(IntReg Xt, IntReg Xn, IntReg Xm, DWORD option)
1620 {
1621     Emit32((DWORD) ( (0xF8600800) |
1622                      (option << 12) |
1623                      (Xm << 16) |
1624                      (Xn << 5) |
1625                      (Xt)
1626                 ));
1627     
1628 }
1629
1630 void StubLinkerCPU::EmitMovReg(IntReg Xd, IntReg Xm)
1631 {
1632     if (Xd == RegSp || Xm == RegSp)
1633     {
1634         // This is a different encoding than the regular MOV (register) below.
1635         // Note that RegSp and RegZero share the same encoding. 
1636         // TODO: check that the intention is not mov Xd, XZR
1637         //  MOV <Xd|SP>, <Xn|SP>
1638         // which is equivalent to 
1639         //  ADD <Xd|SP>, <Xn|SP>, #0
1640         // Encoding: sf|0|0|1|0|0|0|1|shift(2)|imm(12)|Xn|Xd
1641         // where 
1642         //  sf = 1 -> 64-bit variant
1643         //  shift and imm12 are both 0 
1644         Emit32((DWORD) (0x91000000 | (Xm << 5) | Xd));
1645     }
1646     else
1647     {
1648         //  MOV <Xd>, <Xm>
1649         // which is eqivalent to 
1650         //  ORR <Xd>. XZR, <Xm>
1651         // Encoding: sf|0|1|0|1|0|1|0|shift(2)|0|Xm|imm(6)|Xn|Xd
1652         // where
1653         //  sf = 1 -> 64-bit variant
1654         //  shift and imm6 are both 0
1655         //  Xn = XZR
1656         Emit32((DWORD) ( (0xAA << 24) | (Xm << 16) | (0x1F << 5) | Xd));
1657     }
1658 }
1659
1660 void StubLinkerCPU::EmitSubImm(IntReg Xd, IntReg Xn, unsigned int value)
1661 {
1662     // sub <Xd|SP>, <Xn|SP>, #imm{, <shift>}
1663     // Encoding: sf|1|0|1|0|0|0|1|shift(2)|imm(12)|Rn|Rd
1664     // where <shift> is encoded as LSL #0 (no shift) when shift=00 and LSL #12 when shift=01. (No shift in this impl)
1665     // imm(12) is an unsigned immediate in the range of 0 to 4095
1666     // Rn and Rd are both encoded as SP=31
1667     // sf = 1 for 64-bit variant 
1668     _ASSERTE((0 <= value) && (value <= 4095));
1669     Emit32((DWORD) ((0xD1 << 24) | (value << 10) | (Xd << 5) | Xn));
1670
1671 }
1672
1673 void StubLinkerCPU::EmitAddImm(IntReg Xd, IntReg Xn, unsigned int value)
1674 {
1675     // add SP, SP, #imm{, <shift>}
1676     // Encoding: sf|0|0|1|0|0|0|1|shift(2)|imm(12)|Rn|Rd
1677     // where <shift> is encoded as LSL #0 (no shift) when shift=00 and LSL #12 when shift=01. (No shift in this impl)
1678     // imm(12) is an unsigned immediate in the range of 0 to 4095
1679     // Rn and Rd are both encoded as SP=31
1680     // sf = 1 for 64-bit variant 
1681     _ASSERTE((0 <= value) && (value <= 4095));
1682     Emit32((DWORD) ((0x91 << 24) | (value << 10) | (Xn << 5) | Xd));
1683 }
1684
1685 void StubLinkerCPU::EmitCallRegister(IntReg reg)
1686 {
1687     // blr Xn
1688     // Encoding: 1|1|0|1|0|1|1|0|0|0|1|1|1|1|1|1|0|0|0|0|0|Rn|0|0|0|0|0
1689     Emit32((DWORD) (0xD63F0000 | (reg << 5)));
1690 }
1691
1692 void StubLinkerCPU::Init()
1693 {
1694     new (gConditionalBranchIF) ConditionalBranchInstructionFormat();
1695     new (gBranchIF) BranchInstructionFormat();
1696     new (gLoadFromLabelIF) LoadFromLabelInstructionFormat();
1697 }
1698
1699 // Emits code to adjust arguments for static delegate target.
1700 VOID StubLinkerCPU::EmitShuffleThunk(ShuffleEntry *pShuffleEntryArray)
1701 {
1702     // On entry x0 holds the delegate instance. Look up the real target address stored in the MethodPtrAux
1703     // field and save it in x16(ip). Tailcall to the target method after re-arranging the arguments
1704     // ldr x16, [x0, #offsetof(DelegateObject, _methodPtrAux)]
1705     EmitLoadStoreRegImm(eLOAD, IntReg(16), IntReg(0), DelegateObject::GetOffsetOfMethodPtrAux());
1706     //add x11, x0, DelegateObject::GetOffsetOfMethodPtrAux() - load the indirection cell into x11 used by ResolveWorkerAsmStub
1707     EmitAddImm(IntReg(11), IntReg(0), DelegateObject::GetOffsetOfMethodPtrAux());
1708
1709     for (ShuffleEntry* pEntry = pShuffleEntryArray; pEntry->srcofs != ShuffleEntry::SENTINEL; pEntry++)
1710     {
1711         if (pEntry->srcofs & ShuffleEntry::REGMASK)
1712         {
1713             // If source is present in register then destination must also be a register
1714             _ASSERTE(pEntry->dstofs & ShuffleEntry::REGMASK);
1715
1716             EmitMovReg(IntReg(pEntry->dstofs & ShuffleEntry::OFSMASK), IntReg(pEntry->srcofs & ShuffleEntry::OFSMASK));
1717         }
1718         else if (pEntry->dstofs & ShuffleEntry::REGMASK)
1719         {
1720             // source must be on the stack
1721             _ASSERTE(!(pEntry->srcofs & ShuffleEntry::REGMASK));
1722
1723             EmitLoadStoreRegImm(eLOAD, IntReg(pEntry->dstofs & ShuffleEntry::OFSMASK), RegSp, pEntry->srcofs * sizeof(void*));
1724         }
1725         else
1726         {
1727             // source must be on the stack
1728             _ASSERTE(!(pEntry->srcofs & ShuffleEntry::REGMASK));
1729
1730             // dest must be on the stack
1731             _ASSERTE(!(pEntry->dstofs & ShuffleEntry::REGMASK));
1732
1733             EmitLoadStoreRegImm(eLOAD, IntReg(9), RegSp, pEntry->srcofs * sizeof(void*));
1734             EmitLoadStoreRegImm(eSTORE, IntReg(9), RegSp, pEntry->dstofs * sizeof(void*));
1735         }
1736     }
1737
1738     // Tailcall to target
1739     // br x16
1740     EmitJumpRegister(IntReg(16));
1741 }
1742
1743 // Emits code to adjust arguments for static delegate target.
1744 VOID StubLinkerCPU::EmitComputedInstantiatingMethodStub(MethodDesc* pSharedMD, struct ShuffleEntry *pShuffleEntryArray, void* extraArg)
1745 {
1746     STANDARD_VM_CONTRACT;
1747
1748     for (ShuffleEntry* pEntry = pShuffleEntryArray; pEntry->srcofs != ShuffleEntry::SENTINEL; pEntry++)
1749     {
1750         _ASSERTE(pEntry->dstofs & ShuffleEntry::REGMASK);
1751         _ASSERTE(pEntry->srcofs & ShuffleEntry::REGMASK);
1752         _ASSERTE(!(pEntry->dstofs & ShuffleEntry::FPREGMASK));
1753         _ASSERTE(!(pEntry->srcofs & ShuffleEntry::FPREGMASK));
1754         _ASSERTE(pEntry->dstofs != ShuffleEntry::HELPERREG);
1755         _ASSERTE(pEntry->srcofs != ShuffleEntry::HELPERREG);
1756
1757         EmitMovReg(IntReg(pEntry->dstofs & ShuffleEntry::OFSMASK), IntReg(pEntry->srcofs & ShuffleEntry::OFSMASK));
1758     }
1759
1760     MetaSig msig(pSharedMD);
1761     ArgIterator argit(&msig);
1762
1763     if (argit.HasParamType())
1764     {
1765         ArgLocDesc sInstArgLoc;
1766         argit.GetParamTypeLoc(&sInstArgLoc);
1767         int regHidden = sInstArgLoc.m_idxGenReg;
1768         _ASSERTE(regHidden != -1);
1769
1770         if (extraArg == NULL)
1771         {
1772             if (pSharedMD->RequiresInstMethodTableArg())
1773             {
1774                 // Unboxing stub case
1775                 // Fill param arg with methodtable of this pointer
1776                 // ldr regHidden, [x0, #0]
1777                 EmitLoadStoreRegImm(eLOAD, IntReg(regHidden), IntReg(0), 0);
1778             }
1779         }
1780         else
1781         {
1782             EmitMovConstant(IntReg(regHidden), (UINT64)extraArg);
1783         }
1784     }
1785
1786     if (extraArg == NULL)
1787     {
1788         // Unboxing stub case
1789         // Address of the value type is address of the boxed instance plus sizeof(MethodDesc*).
1790         //  add x0, #sizeof(MethodDesc*)
1791         EmitAddImm(IntReg(0), IntReg(0), sizeof(MethodDesc*));
1792     }
1793
1794     // Tail call the real target.
1795     EmitCallManagedMethod(pSharedMD, TRUE /* tail call */);
1796 }
1797
1798 void StubLinkerCPU::EmitCallLabel(CodeLabel *target, BOOL fTailCall, BOOL fIndirect, BOOL fRelativeIndirect)
1799 {
1800     _ASSERTE(fRelativeIndirect && fIndirect || !fRelativeIndirect);
1801
1802     BranchInstructionFormat::VariationCodes variationCode = BranchInstructionFormat::VariationCodes::BIF_VAR_JUMP;
1803     if (!fTailCall)
1804         variationCode = static_cast<BranchInstructionFormat::VariationCodes>(variationCode | BranchInstructionFormat::VariationCodes::BIF_VAR_CALL);
1805     if (fIndirect)
1806         variationCode = static_cast<BranchInstructionFormat::VariationCodes>(variationCode | BranchInstructionFormat::VariationCodes::BIF_VAR_INDIRECT);
1807     if (fRelativeIndirect)
1808         variationCode = static_cast<BranchInstructionFormat::VariationCodes>(variationCode | BranchInstructionFormat::VariationCodes::BIF_VAR_RELATIVE_INDIRECT);
1809
1810     EmitLabelRef(target, reinterpret_cast<BranchInstructionFormat&>(gBranchIF), (UINT)variationCode);
1811 }
1812
1813 void StubLinkerCPU::EmitCallManagedMethod(MethodDesc *pMD, BOOL fTailCall)
1814 {
1815     // Use direct call if possible.
1816     if (pMD->HasStableEntryPoint())
1817     {
1818         EmitCallLabel(NewExternalCodeLabel((LPVOID)pMD->GetStableEntryPoint()), fTailCall, FALSE, FALSE);
1819     }
1820     else
1821     {
1822         BOOL isRelative = MethodTable::VTableIndir2_t::isRelative
1823                           && pMD->IsVtableSlot();
1824
1825 #ifndef FEATURE_NGEN_RELOCS_OPTIMIZATIONS
1826         _ASSERTE(!isRelative);
1827 #endif
1828
1829         EmitCallLabel(NewExternalCodeLabel((LPVOID)pMD->GetAddrOfSlot()), fTailCall, TRUE, isRelative);
1830     }
1831 }
1832
1833 #ifndef CROSSGEN_COMPILE
1834
1835 #ifdef FEATURE_READYTORUN
1836
1837 //
1838 // Allocation of dynamic helpers
1839 //
1840
1841 #define DYNAMIC_HELPER_ALIGNMENT sizeof(TADDR)
1842
1843 #define BEGIN_DYNAMIC_HELPER_EMIT(size) \
1844     SIZE_T cb = size; \
1845     SIZE_T cbAligned = ALIGN_UP(cb, DYNAMIC_HELPER_ALIGNMENT); \
1846     BYTE * pStart = (BYTE *)(void *)pAllocator->GetDynamicHelpersHeap()->AllocAlignedMem(cbAligned, DYNAMIC_HELPER_ALIGNMENT); \
1847     BYTE * p = pStart;
1848
1849 #define END_DYNAMIC_HELPER_EMIT() \
1850     _ASSERTE(pStart + cb == p); \
1851     while (p < pStart + cbAligned) { *(DWORD*)p = 0xBADC0DF0; p += 4; }\
1852     ClrFlushInstructionCache(pStart, cbAligned); \
1853     return (PCODE)pStart
1854
1855 // Uses x8 as scratch register to store address of data label
1856 // After load x8 is increment to point to next data
1857 // only accepts positive offsets
1858 static void LoadRegPair(BYTE* p, int reg1, int reg2, UINT32 offset)
1859 {
1860     LIMITED_METHOD_CONTRACT;
1861
1862     // adr x8, <label>
1863     *(DWORD*)(p + 0) = 0x10000008 | ((offset >> 2) << 5);
1864     // ldp reg1, reg2, [x8], #16 ; postindex & wback
1865     *(DWORD*)(p + 4) = 0xa8c10100 | (reg2 << 10) | reg1;
1866 }
1867
1868 PCODE DynamicHelpers::CreateHelper(LoaderAllocator * pAllocator, TADDR arg, PCODE target)
1869 {
1870     STANDARD_VM_CONTRACT;
1871
1872     BEGIN_DYNAMIC_HELPER_EMIT(32);
1873
1874     // adr x8, <label>
1875     // ldp x0, x12, [x8]
1876     LoadRegPair(p, 0, 12, 16);
1877     p += 8;
1878     // br x12
1879     *(DWORD*)p = 0xd61f0180;
1880     p += 4;
1881
1882     // padding to make 8 byte aligned
1883     *(DWORD*)p = 0xBADC0DF0; p += 4;
1884     
1885     // label:
1886     // arg
1887     *(TADDR*)p = arg;
1888     p += 8;
1889     // target
1890     *(PCODE*)p = target;
1891     p += 8;
1892     
1893     END_DYNAMIC_HELPER_EMIT();
1894 }
1895
1896 // Caller must ensure sufficient byte are allocated including padding (if applicable)
1897 void DynamicHelpers::EmitHelperWithArg(BYTE*& p, LoaderAllocator * pAllocator, TADDR arg, PCODE target)
1898 {
1899     STANDARD_VM_CONTRACT;
1900     
1901     // if p is already aligned at 8-byte then padding is required for data alignment  
1902     bool padding = (((uintptr_t)p & 0x7) == 0);
1903     
1904     // adr x8, <label>
1905     // ldp x1, x12, [x8]
1906     LoadRegPair(p, 1, 12, padding?16:12);
1907     p += 8;
1908
1909     // br x12
1910     *(DWORD*)p = 0xd61f0180;
1911     p += 4; 
1912
1913     if(padding)
1914     {
1915         // padding to make 8 byte aligned
1916         *(DWORD*)p = 0xBADC0DF0; 
1917         p += 4;
1918     }
1919     
1920     // label:
1921     // arg
1922     *(TADDR*)p = arg;
1923     p += 8;
1924     // target
1925     *(PCODE*)p = target;
1926     p += 8; 
1927 }
1928
1929 PCODE DynamicHelpers::CreateHelperWithArg(LoaderAllocator * pAllocator, TADDR arg, PCODE target)
1930 {
1931     STANDARD_VM_CONTRACT;
1932
1933     BEGIN_DYNAMIC_HELPER_EMIT(32);
1934     
1935     EmitHelperWithArg(p, pAllocator, arg, target);
1936     
1937     END_DYNAMIC_HELPER_EMIT();    
1938 }
1939
1940 PCODE DynamicHelpers::CreateHelper(LoaderAllocator * pAllocator, TADDR arg, TADDR arg2, PCODE target)
1941 {
1942     STANDARD_VM_CONTRACT;
1943
1944     BEGIN_DYNAMIC_HELPER_EMIT(40);
1945     
1946     // adr x8, <label>
1947     // ldp x0, x1, [x8] ; wback
1948     LoadRegPair(p, 0, 1, 16);
1949     p += 8;
1950
1951     // ldr x12, [x8]
1952     *(DWORD*)p = 0xf940010c;
1953     p += 4;
1954     // br x12
1955     *(DWORD*)p = 0xd61f0180;
1956     p += 4;     
1957     // label:
1958     // arg
1959     *(TADDR*)p = arg;
1960     p += 8;
1961     // arg2
1962     *(TADDR*)p = arg2;
1963     p += 8;
1964     // target
1965     *(TADDR*)p = target;
1966     p += 8;
1967
1968     END_DYNAMIC_HELPER_EMIT();      
1969 }
1970
1971 PCODE DynamicHelpers::CreateHelperArgMove(LoaderAllocator * pAllocator, TADDR arg, PCODE target)
1972 {
1973     STANDARD_VM_CONTRACT;
1974
1975     BEGIN_DYNAMIC_HELPER_EMIT(32);
1976     
1977     // mov x1, x0
1978     *(DWORD*)p = 0x91000001;
1979     p += 4;
1980
1981     // adr x8, <label>
1982     // ldp x0, x12, [x8]
1983     LoadRegPair(p, 0, 12, 12);
1984     p += 8;
1985
1986     // br x12
1987     *(DWORD*)p = 0xd61f0180;
1988     p += 4;     
1989
1990     // label:
1991     // arg
1992     *(TADDR*)p = arg;
1993     p += 8; 
1994     // target
1995     *(TADDR*)p = target;
1996     p += 8;
1997     
1998     END_DYNAMIC_HELPER_EMIT();          
1999 }
2000
2001 PCODE DynamicHelpers::CreateReturn(LoaderAllocator * pAllocator)
2002 {
2003     STANDARD_VM_CONTRACT;
2004
2005     BEGIN_DYNAMIC_HELPER_EMIT(4);
2006     
2007     // br lr
2008     *(DWORD*)p = 0xd61f03c0;
2009     p += 4;
2010     END_DYNAMIC_HELPER_EMIT();              
2011 }
2012
2013 PCODE DynamicHelpers::CreateReturnConst(LoaderAllocator * pAllocator, TADDR arg)
2014 {
2015     STANDARD_VM_CONTRACT;
2016
2017     BEGIN_DYNAMIC_HELPER_EMIT(16);
2018  
2019     // ldr x0, <label>
2020     *(DWORD*)p = 0x58000040;
2021     p += 4;
2022     
2023     // br lr
2024     *(DWORD*)p = 0xd61f03c0;
2025     p += 4;
2026
2027     // label:
2028     // arg
2029     *(TADDR*)p = arg;
2030     p += 8; 
2031
2032     END_DYNAMIC_HELPER_EMIT();              
2033 }
2034
2035 PCODE DynamicHelpers::CreateReturnIndirConst(LoaderAllocator * pAllocator, TADDR arg, INT8 offset)
2036 {
2037     STANDARD_VM_CONTRACT;
2038
2039     BEGIN_DYNAMIC_HELPER_EMIT(24);
2040     
2041     // ldr x0, <label>
2042     *(DWORD*)p = 0x58000080;
2043     p += 4;
2044
2045     // ldr x0, [x0]
2046     *(DWORD*)p = 0xf9400000;
2047     p += 4;
2048     
2049     // add x0, x0, offset
2050     *(DWORD*)p = 0x91000000 | (offset << 10);
2051     p += 4;
2052     
2053     // br lr
2054     *(DWORD*)p = 0xd61f03c0;
2055     p += 4;
2056     
2057     // label:
2058     // arg
2059     *(TADDR*)p = arg;
2060     p += 8; 
2061     
2062     END_DYNAMIC_HELPER_EMIT();              
2063 }
2064
2065 PCODE DynamicHelpers::CreateHelperWithTwoArgs(LoaderAllocator * pAllocator, TADDR arg, PCODE target)
2066 {
2067     STANDARD_VM_CONTRACT;
2068
2069     BEGIN_DYNAMIC_HELPER_EMIT(32);
2070     
2071     // adr x8, <label>
2072     // ldp x2, x12, [x8]
2073     LoadRegPair(p, 2, 12, 16);
2074     p += 8;
2075
2076     // br x12
2077     *(DWORD*)p = 0xd61f0180;
2078     p += 4; 
2079
2080     // padding to make 8 byte aligned
2081     *(DWORD*)p = 0xBADC0DF0; p += 4;
2082     
2083     // label:
2084     // arg
2085     *(TADDR*)p = arg;
2086     p += 8; 
2087
2088     // target
2089     *(TADDR*)p = target;
2090     p += 8;
2091     END_DYNAMIC_HELPER_EMIT();              
2092 }
2093
2094 PCODE DynamicHelpers::CreateHelperWithTwoArgs(LoaderAllocator * pAllocator, TADDR arg, TADDR arg2, PCODE target)
2095 {
2096     STANDARD_VM_CONTRACT;
2097
2098     BEGIN_DYNAMIC_HELPER_EMIT(40);
2099     
2100     // adr x8, <label>
2101     // ldp x2, x3, [x8]; wback
2102     LoadRegPair(p, 2, 3, 16);
2103     p += 8;
2104     
2105     // ldr x12, [x8]
2106     *(DWORD*)p = 0xf940010c;
2107     p += 4;
2108
2109     // br x12
2110     *(DWORD*)p = 0xd61f0180;
2111     p += 4;     
2112
2113     // label:
2114     // arg
2115     *(TADDR*)p = arg;
2116     p += 8;
2117     // arg2
2118     *(TADDR*)p = arg2;
2119     p += 8;
2120     // target
2121     *(TADDR*)p = target;
2122     p += 8;
2123     END_DYNAMIC_HELPER_EMIT();              
2124 }
2125
2126 PCODE DynamicHelpers::CreateDictionaryLookupHelper(LoaderAllocator * pAllocator, CORINFO_RUNTIME_LOOKUP * pLookup, DWORD dictionaryIndexAndSlot, Module * pModule)
2127 {
2128     STANDARD_VM_CONTRACT;
2129
2130     PCODE helperAddress = (pLookup->helper == CORINFO_HELP_RUNTIMEHANDLE_METHOD ?
2131         GetEEFuncEntryPoint(JIT_GenericHandleMethodWithSlotAndModule) :
2132         GetEEFuncEntryPoint(JIT_GenericHandleClassWithSlotAndModule));
2133
2134     GenericHandleArgs * pArgs = (GenericHandleArgs *)(void *)pAllocator->GetDynamicHelpersHeap()->AllocAlignedMem(sizeof(GenericHandleArgs), DYNAMIC_HELPER_ALIGNMENT);
2135     pArgs->dictionaryIndexAndSlot = dictionaryIndexAndSlot;
2136     pArgs->signature = pLookup->signature;
2137     pArgs->module = (CORINFO_MODULE_HANDLE)pModule;
2138
2139     // It's available only via the run-time helper function
2140     if (pLookup->indirections == CORINFO_USEHELPER)
2141     {
2142         BEGIN_DYNAMIC_HELPER_EMIT(32);
2143
2144         // X0 already contains generic context parameter
2145         // reuse EmitHelperWithArg for below two operations
2146         // X1 <- pArgs
2147         // branch to helperAddress
2148         EmitHelperWithArg(p, pAllocator, (TADDR)pArgs, helperAddress);
2149
2150         END_DYNAMIC_HELPER_EMIT();
2151     }
2152     else
2153     {
2154         int indirectionsCodeSize = 0;
2155         int indirectionsDataSize = 0;
2156         for (WORD i = 0; i < pLookup->indirections; i++)
2157         {
2158             if ((i == 0 && pLookup->indirectFirstOffset) || (i == 1 && pLookup->indirectSecondOffset))
2159             {
2160                 indirectionsCodeSize += (pLookup->offsets[i] > 4095 ? 16 : 12); // if( > 4095) (16 code bytes) else 12 bytes for instruction with offset encoded in instruction
2161                 indirectionsDataSize += (pLookup->offsets[i] > 4095 ? 4 : 0); // 4 bytes for storing indirection offset values
2162             }
2163             else
2164             {
2165                 indirectionsCodeSize += (pLookup->offsets[i] > 32760 ? 8 : 4); // if( > 32760) (8 code bytes) else 4 bytes for instruction with offset encoded in instruction
2166                 indirectionsDataSize += (pLookup->offsets[i] > 32760 ? 4 : 0); // 4 bytes for storing indirection offset values
2167             }
2168         }
2169
2170         int codeSize = indirectionsCodeSize;
2171         if(pLookup->testForNull)
2172         {
2173             codeSize += 4; // mov
2174             codeSize += 12; // cbz-ret-mov
2175             //padding for 8-byte align (required by EmitHelperWithArg)
2176             if((codeSize & 0x7) == 0)
2177                 codeSize += 4;
2178             codeSize += 28; // size of EmitHelperWithArg
2179         }
2180         else
2181         {
2182             codeSize += 4 ; /* ret */
2183         }
2184         
2185         codeSize += indirectionsDataSize;
2186
2187         BEGIN_DYNAMIC_HELPER_EMIT(codeSize);
2188
2189         if (pLookup->testForNull)
2190         {
2191             // mov x9, x0
2192             *(DWORD*)p = 0x91000009; 
2193             p += 4;
2194         }
2195         
2196         // moving offset value wrt PC. Currently points to first indirection offset data. 
2197         uint dataOffset = codeSize - indirectionsDataSize - (pLookup->testForNull ? 4 : 0);
2198         for (WORD i = 0; i < pLookup->indirections; i++)
2199         {
2200             if ((i == 0 && pLookup->indirectFirstOffset) || (i == 1 && pLookup->indirectSecondOffset))
2201             {
2202                 if(pLookup->offsets[i] > 4095)
2203                 {
2204                     // ldr w10, [PC, #dataOffset]
2205                     *(DWORD*)p = 0x1800000a | ((dataOffset>>2)<<5);
2206                     p += 4;
2207                     // add x0, x0, x10
2208                     *(DWORD*)p = 0x8b0a0000;
2209                     p += 4;
2210
2211                     // move to next indirection offset data
2212                     dataOffset = dataOffset - 16 + 4; // subtract 16 as we have moved PC by 16 and add 4 as next data is at 4 bytes from previous data
2213                 }
2214                 else
2215                 {
2216                     // add x0, #(pLookup->offsets[i])
2217                     *(DWORD*)p = 0x91000000 | (((UINT32)pLookup->offsets[i])<<10);
2218                     p += 4;
2219
2220                     dataOffset -= 12; // subtract 12 as we have moved PC by 12
2221                 }
2222
2223                 // ldr x10, [x0]
2224                 *(DWORD*)p = 0xf940000a;
2225                 p += 4;
2226                 // add x0, x0, x10
2227                 *(DWORD*)p = 0x8b0a0000;
2228                 p += 4;
2229             }
2230             else
2231             {
2232                 if(pLookup->offsets[i] > 32760)
2233                 {
2234                     // ldr w10, [PC, #dataOffset]
2235                     *(DWORD*)p = 0x1800000a | ((dataOffset>>2)<<5);
2236                     p += 4;
2237                     // ldr x0, [x0, x10]
2238                     *(DWORD*)p = 0xf86a6800;
2239                     p += 4;
2240
2241                     // move to next indirection offset data
2242                     dataOffset = dataOffset - 8 + 4; // subtract 8 as we have moved PC by 8 and add 4 as next data is at 4 bytes from previous data
2243                 }
2244                 else
2245                 {
2246                     // offset must be 8 byte aligned
2247                     _ASSERTE((pLookup->offsets[i] & 0x7) == 0);
2248
2249                     // ldr x0, [x0, #(pLookup->offsets[i])]
2250                     *(DWORD*)p = 0xf9400000 | ( ((UINT32)pLookup->offsets[i]>>3) <<10 );
2251                     p += 4;
2252                     dataOffset -= 4; // subtract 4 as we have moved PC by 4
2253                 }
2254             }
2255         }
2256         
2257         // No null test required
2258         if (!pLookup->testForNull)
2259         {
2260             // ret lr
2261             *(DWORD*)p = 0xd65f03c0;
2262             p += 4;
2263         }
2264         else
2265         {
2266             // cbz x0, nullvaluelabel
2267             *(DWORD*)p = 0xb4000040;
2268             p += 4;
2269             // ret lr
2270             *(DWORD*)p = 0xd65f03c0;
2271             p += 4;
2272             // nullvaluelabel:
2273             // mov x0, x9
2274             *(DWORD*)p = 0x91000120;
2275             p += 4;
2276             // reuse EmitHelperWithArg for below two operations
2277             // X1 <- pArgs
2278             // branch to helperAddress
2279             EmitHelperWithArg(p, pAllocator, (TADDR)pArgs, helperAddress);
2280         }     
2281         
2282         // datalabel:
2283         for (WORD i = 0; i < pLookup->indirections; i++)
2284         {
2285             if ((i == 0 && pLookup->indirectFirstOffset) || (i == 1 && pLookup->indirectSecondOffset))
2286             {
2287                 if(pLookup->offsets[i] > 4095)
2288                 {
2289                     *(UINT32*)p = (UINT32)pLookup->offsets[i];
2290                     p += 4;
2291                 }
2292             }
2293             else
2294             {
2295                 if(pLookup->offsets[i] > 32760)
2296                 {
2297                     _ASSERTE((pLookup->offsets[i] & 0xffffffff00000000) == 0);
2298                     *(UINT32*)p = (UINT32)pLookup->offsets[i];
2299                     p += 4;
2300                 }
2301             }
2302         }
2303         
2304         END_DYNAMIC_HELPER_EMIT();        
2305     }
2306 }
2307 #endif // FEATURE_READYTORUN
2308
2309 #endif // CROSSGEN_COMPILE
2310
2311 #endif // #ifndef DACCESS_COMPILE